package
1.0.0
Repository: https://github.com/go-fed/activity.git
Documentation: pkg.go.dev

# README

pub

Implements the Social and Federating Protocols in the ActivityPub specification.

Reference & Tutorial

The go-fed website contains tutorials and reference materials, in addition to the rest of this README.

How To Use

go get github.com/go-fed/activity

The root of all ActivityPub behavior is the Actor, which requires you to implement a few interfaces:

import (
  "github.com/go-fed/activity/pub"
)

type myActivityPubApp struct { /* ... */ }
type myAppsDatabase struct { /* ... */ }
type myAppsClock struct { /* ... */ }

var (
  // Your app will implement pub.CommonBehavior, and either
  // pub.SocialProtocol, pub.FederatingProtocol, or both.
  myApp = &myActivityPubApp{}
  myCommonBehavior pub.CommonBehavior = myApp
  mySocialProtocol pub.SocialProtocol = myApp
  myFederatingProtocol pub.FederatingProtocol = myApp
  // Your app's database implementation.
  myDatabase pub.Database = &myAppsDatabase{}
  // Your app's clock.
  myClock pub.Clock = &myAppsClock{}
)

// Only support the C2S Social protocol
actor := pub.NewSocialActor(
  myCommonBehavior,
  mySocialProtocol,
  myDatabase,
  myClock)
// OR
//
// Only support S2S Federating protocol
actor = pub.NewFederatingActor(
  myCommonBehavior,
  myFederatingProtocol,
  myDatabase,
  myClock)
// OR
//
// Support both C2S Social and S2S Federating protocol.
actor = pub.NewActor(
  myCommonBehavior,
  mySocialProtocol,
  myFederatingProtocol,
  myDatabase,
  myClock)

Next, hook the Actor into your web server:

// The application's actor
var actor pub.Actor
var outboxHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
  c := context.Background()
  // Populate c with request-specific information
  if handled, err := actor.PostOutbox(c, w, r); err != nil {
    // Write to w
    return
  } else if handled {
    return
  } else if handled, err = actor.GetOutbox(c, w, r); err != nil {
    // Write to w
    return
  } else if handled {
    return
  }
  // else:
  //
  // Handle non-ActivityPub request, such as serving a webpage.
}
var inboxHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
  c := context.Background()
  // Populate c with request-specific information
  if handled, err := actor.PostInbox(c, w, r); err != nil {
    // Write to w
    return
  } else if handled {
    return
  } else if handled, err = actor.GetInbox(c, w, r); err != nil {
    // Write to w
    return
  } else if handled {
    return
  }
  // else:
  //
  // Handle non-ActivityPub request, such as serving a webpage.
}
// Add the handlers to a HTTP server
serveMux := http.NewServeMux()
serveMux.HandleFunc("/actor/outbox", outboxHandler)
serveMux.HandleFunc("/actor/inbox", inboxHandler)
var server http.Server
server.Handler = serveMux

To serve ActivityStreams data:

myHander := pub.NewActivityStreamsHandler(myDatabase, myClock)
var activityStreamsHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
  c := context.Background()
  // Populate c with request-specific information
  if handled, err := myHandler(c, w, r); err != nil {
    // Write to w
    return
  } else if handled {
    return
  }
  // else:
  //
  // Handle non-ActivityPub request, such as serving a webpage.
}
serveMux.HandleFunc("/some/data/like/a/note", activityStreamsHandler)

Dependency Injection

Package pub relies on dependency injection to provide out-of-the-box support for ActivityPub. The interfaces to be satisfied are:

  • CommonBehavior - Behavior needed regardless of which Protocol is used.
  • SocialProtocol - Behavior needed for the Social Protocol.
  • FederatingProtocol - Behavior needed for the Federating Protocol.
  • Database - The data store abstraction, not tied to the database/sql package.
  • Clock - The server's internal clock.
  • Transport - Responsible for the network that serves requests and deliveries of ActivityStreams data. A HttpSigTransport type is provided.

These implementations form the core of an application's behavior without worrying about the particulars and pitfalls of the ActivityPub protocol. Implementing these interfaces gives you greater assurance about being ActivityPub compliant.

Application Logic

The SocialProtocol and FederatingProtocol are responsible for returning callback functions compatible with streams.TypeResolver. They also return SocialWrappedCallbacks and FederatingWrappedCallbacks, which are nothing more than a bundle of default behaviors for types like Create, Update, and so on.

Applications will want to focus on implementing their specific behaviors in the callbacks, and have fine-grained control over customization:

// Implements the FederatingProtocol interface.
//
// This illustration can also be applied for the Social Protocol.
func (m *myAppsFederatingProtocol) Callbacks(c context.Context) (wrapped pub.FederatingWrappedCallbacks, other []interface{}) {
  // The context 'c' has request-specific logic and can be used to apply complex
  // logic building the right behaviors, if desired.
  //
  // 'c' will later be passed through to the callbacks created below.
  wrapped = pub.FederatingWrappedCallbacks{
    Create: func(ctx context.Context, create vocab.ActivityStreamsCreate) error {
      // This function is wrapped by default behavior.
      //
      // More application specific logic can be written here.
      //
      // 'ctx' will have request-specific information from the HTTP handler. It
      // is the same as the 'c' passed to the Callbacks method.
      // 'create' has, at this point, already triggered the recommended
      // ActivityPub side effect behavior. The application can process it
      // further as needed.
      return nil
    },
  }
  // The 'other' must contain functions that satisfy the signature pattern
  // required by streams.JSONResolver.
  //
  // If they are not, at runtime errors will be returned to indicate this.
  other = []interface{}{
    // The FederatingWrappedCallbacks has default behavior for an "Update" type,
    // but since we are providing this behavior in "other" and not in the
    // FederatingWrappedCallbacks.Update member, we will entirely replace the
    // default behavior provided by go-fed. Be careful that this still
    // implements ActivityPub properly.
    func(ctx context.Context, update vocab.ActivityStreamsUpdate) error {
      // This function is NOT wrapped by default behavior.
      //
      // Application specific logic can be written here.
      //
      // 'ctx' will have request-specific information from the HTTP handler. It
      // is the same as the 'c' passed to the Callbacks method.
      // 'update' will NOT trigger the recommended ActivityPub side effect
      // behavior. The application should do so in addition to any other custom
      // side effects required.
      return nil
    },
    // The "Listen" type has no default suggested behavior in ActivityPub, so
    // this just makes this application able to handle "Listen" activities.
    func(ctx context.Context, listen vocab.ActivityStreamsListen) error {
      // This function is NOT wrapped by default behavior. There's not a
      // FederatingWrappedCallbacks.Listen member to wrap.
      //
      // Application specific logic can be written here.
      //
      // 'ctx' will have request-specific information from the HTTP handler. It
      // is the same as the 'c' passed to the Callbacks method.
      // 'listen' can be processed with side effects as the application needs.
      return nil
    },
  }
  return
}

The pub package supports applications that grow into more custom solutions by overriding the default behaviors as needed.

ActivityStreams Extensions: Future-Proofing An Application

Package pub relies on the streams.TypeResolver and streams.JSONResolver code generated types. As new ActivityStreams extensions are developed and their code is generated, pub will automatically pick up support for these extensions.

The steps to rapidly implement a new extension in a pub application are:

  1. Generate an OWL definition of the ActivityStreams extension. This definition could be the same one defining the vocabulary at the @context IRI.
  2. Run astool to autogenerate the golang types in the streams package.
  3. Implement the application's callbacks in the FederatingProtocol.Callbacks or SocialProtocol.Callbacks for the new behaviors needed.
  4. Build the application, which builds pub, with the newly generated streams code. No code changes in pub are required.

Whether an author of an ActivityStreams extension or an application developer, these quick steps should reduce the barrier to adopion in a statically-typed environment.

DelegateActor

For those that need a near-complete custom ActivityPub solution, or want to have that possibility in the future after adopting go-fed, the DelegateActor interface can be used to obtain an Actor:

// Use custom ActivityPub implementation
actor = pub.NewCustomActor(
  myDelegateActor,
  isSocialProtocolEnabled,
  isFederatedProtocolEnabled,
  myAppsClock)

It does not guarantee that an implementation adheres to the ActivityPub specification. It acts as a stepping stone for applications that want to build up to a fully custom solution and not be locked into the pub package implementation.

# Functions

GetId will attempt to find the 'id' property or, if it happens to be a Link or derived from Link type, the 'href' property instead.
IsPublic determines if an IRI string is the Public collection as defined in the spec, including JSON-LD compliant collections.
NewActivityStreamsHandler creates a HandlerFunc to serve ActivityStreams requests which are coming from other clients or servers that wish to obtain an ActivityStreams representation of data.
NewActor builds a new Actor concept that handles both the Social and Federating Protocol parts of ActivityPub.
NewCustomActor allows clients to create a custom ActivityPub implementation for the Social Protocol, Federating Protocol, or both.
NewFederatingActor builds a new Actor concept that handles only the Federating Protocol part of ActivityPub.
NewHttpSigTransport returns a new Transport.
NewSocialActor builds a new Actor concept that handles only the Social Protocol part of ActivityPub.
ToId returns an IdProperty's id.

# Constants

OnFollowAutomaticallyAccept triggers the side effect of sending an Accept of this Follow request in response.
OnFollowAutomaticallyAccept triggers the side effect of sending a Reject of this Follow request in response.
OnFollowDoNothing does not take any action when a Follow Activity is received.
PublicActivityPubIRI is the IRI that indicates an Activity is meant to be visible for general public consumption.

# Variables

ErrObjectRequired indicates the activity needs its object property set.
ErrTargetRequired indicates the activity needs its target property set.

# Structs

FederatingWrappedCallbacks lists the callback functions that already have some side effect behavior provided by the pub library.
HttpSigTransport makes a dereference call using HTTP signatures to authenticate the request on behalf of a particular actor.
SocialWrappedCallbacks lists the callback functions that already have some side effect behavior provided by the pub library.

# Interfaces

Activity represents any ActivityStreams Activity type.
Actor represents ActivityPub's actor concept.
Clock determines the time.
Common contains functions required for both the Social API and Federating Protocol.
No description provided by the author
DelegateActor contains the detailed interface an application must satisfy in order to implement the ActivityPub specification.
FederatingActor is an Actor that allows programmatically delivering an Activity to a federating peer.
FederatingProtocol contains behaviors an application needs to satisfy for the full ActivityPub S2S implementation to be supported by this library.
HttpClient sends http requests, and is an abstraction only needed by the HttpSigTransport.
IdProperty is a property that can readily have its id obtained.
SocialProtocol contains behaviors an application needs to satisfy for the full ActivityPub C2S implementation to be supported by this library.
Transport makes ActivityStreams calls to other servers in order to send or receive ActivityStreams data.

# Type aliases

HandlerFunc determines whether an incoming HTTP request is an ActivityStreams GET request, and if so attempts to serve ActivityStreams data.
OnFollowBehavior enumerates the different default actions that the go-fed library can provide when receiving a Follow Activity from a peer.