Code Monkey home page Code Monkey logo

activity-kit's Introduction

ActivityKit

This is a Lerna monorepo that holds packages related to a TypeScript implementation of the ActivityPub protocol.

ActivityPub is a standardized method of exchanging social data.

Current Status

This project is still incomplete at the moment. Much of the core functionality is complete, but refer to:

Running in a Project

Prerequesities

In addition to hosting a (Node) server, you will also need:

  • Database: A compatable database that the server can access.
  • Storage: A way to store files such as profile pics.
  • HTML Templating: A way to render pages with provided JSON data.

Example

Canonical example using Express + MongoDB + FTP:

import * as express from "express";
import { MongoClient } from "mongodb";

import { AP, Adapters } from "@activity-kit/types";
import { activityKitPlugin } from "@activity-kit/express-middleware";
import { MongoDbAdapter } from "@activity-kit/db-mongo";
import { TokenAuthAdapter } from "@activity-kit/auth-token";
import { NodeCryptoAdapter } from "@activity-kit/crypto-node";
import { FtpStorageAdapter } from "@activity-kit/storage-ftp";

// Use Express for all routes.
const app = express.default();

(async () => {
  const ftpStorageAdapterOptions = {
    user: process.env.AP_FTP_USER,
    password: process.env.AP_FTP_PASSWORD,
    host: process.env.AP_FTP_HOST,
    path: process.env.AP_FTP_PATH,
  };

  // Use the FTP adapter for handling uploaded media.
  const ftpStorageAdapter = new FtpStorageAdapter(ftpStorageAdapterOptions);

  // Use MongoDB to store data.
  const mongoClient = new MongoClient(process.env.AP_MONGO_CLIENT_URL);
  await mongoClient.connect();
  const mongoDb = mongoClient.db("activitypub");
  const mongoDbAdapter = new MongoDbAdapter(mongoDb);

  // Use Node's Crypto library + Mongo for authentication.
  const nodeCryptoAdapter = new NodeCryptoAdapter();
  const cryptoAuthAdapter = new CryptoAuthAdapter({
    db: mongoDbAdapter,
    crypto: nodeCryptoAdapter,
  });

  // Use the ActivityKit Express plugin.
  app.use(
    activityKitPlugin({
      adapters: {
        auth: cryptoAuthAdapter,
        crypto: nodeCryptoAdapter,
        db: mongoDbAdapter,
        storage: ftpStorageAdapter,
      },

      plugins: [],

      routes: {},

      pages: {
        // Login/Signup via Auth adapter.
        login: async (): Promise<string> => {
          // Use a rendering engine to generate and return a string here.
          return `
            <html>
              <!-- Signup form POSTs to /user endpoint to create new user. -->
              <form>...</form>
              <!-- Login form POSTs to /login endpoint to get cookie token. -->
              <form>...</form>
            </html>
          `;
        },

        // Logged-in users can edit their profile, create new posts, etc.
        home: async (homePageProps: {
          actor: AP.Actor;
          shared: AP.Announce[];
          requests: AP.Follow[];
          members: AP.Actor[];
          blocks: AP.Block[];
        }): Promise<string> => {
          // Use a rendering engine to generate and return a string here.
          return `
            <html>
              <title>@${actor.preferredUsername}</title>
              <!-- Forms POST to user's outbox URL using AP protocol. -->
              <form>...</form>
            </html>
          `;
        },

        // All ActivityPub entities have an HTML view.
        entity: async (entityPageProps: {
          entity: AP.Entity;
          actor?: AP.Actor;
        }): Promise<string> => {
          // Use a rendering engine to generate and return a string here.
          return `
            <html>
              <h1>${entity.type}</h1>
              <p>${entity.summary}</p>
            </html>
          `;
        },
      },
    })
  );

  app.listen(process.env.PORT ?? 3000, () => {
    console.log("Running...");
  });
})();

General Philosophy

This project aims to be spec-compliant.

This project aims to be as versatile and non-opinionated as possible. The hope is to be able to integrate with any project.

This project is MIT-licensed, with the hope it will be forked, reused, or wholly included in other projects due to this permissive license. There may be similiar software that exists, but inclusion would be inviable due to licensing restrictions.

Architecture

This project aims to be agnostic as to how the data is stored, which server is used, etc. Adapters that conform to a specific interface can be mixed and matched.

Additionally, Plugins can modify the endpoints.

Core Layer

The core layer that gets included in all projects include these packages:

  • @activity-kit/types
    • The Activity vocabularies converted to TypeScript types.
  • @activity-kit/endpoints
    • The logic for carrying out the bulk of the ActivityPub protocol.
  • @activity-kit/core
    • Common functions that depend on the Adapter APIs.
  • @activity-kit/utilities
    • Common functions with no dependencies on packages from upper layers.

Adapaters

Database Adapaters

There is a large amount of data related to profiles and interactions that must be persisted over time.

Currently this project comes with:

  • @activity-kit/db-mongo
  • @activity-kit/db-sqlite
  • TODO: @activity-kit/db-postgresql
  • TODO: @activity-kit/db-d1

Authentication Adapters

Users need to be able to sign up and log in to their account.

Current this project comes with:

  • @activity-kit/auth-token
    • Generates tokens via Crypto APIs and stores them in the provided database.
  • @activity-kit/auth-firebase
    • Wrapper around @firebase/auth

Storage Adapters

Users must be able to upload media, such as profile pictures or attachments.

Currently this project comes with:

  • @activity-kit/storage-ftp
    • Uploads media via FTP with the provided credentials.
  • TODO: @activity-kit/storage-s3
    • Upload media via S3-compatible storage APIs.

Server Adapters

The server must handle the core endpoint requests.

Currently this project comes with:

  • @activity-kit/express-middleware

Crypto Adapters

There are a few instances that require using native cryptography APIs, such as generating Actors' public/private key pairs.

Typically this will be handled by Node's crypto library, but the crypto functions are abstracted to also enable support running within a web worker context.

  • @activity-kit/crypto-node
  • TODO: @activity-kit/crypto-browser

Email Adapters

In the future, email will be an optional adapter that can be used to reset passwords and send notifications to users.

  • TODO: @activity-kit/email-nodemailer

Plugins

Plugins provide lifecycle hooks that can modify core functionality.

You can write your own.

Currently this project comes with:

  • @activity-kit/plugin-groups
  • TODO: @activity-kit/single-user

Client/Rendering Layer

This project does not aim to provide any HTML code for client rendering.

The pages configuration properties expect a callback function that renders an HTML string, leaving the front-end mostly to the host app.

The front-end should utilize ActivityPub's Client-to-Server protocol to post Activities on behalf of users.

Use Cases

There are a few use cases this project attempts to fulfill. Ideally this project papers over some of the complexity of JSON-LD, Activity Streams collections, etc. to make getting started easy.

Connecting a Blog to the Fediverse

Someone already has an HTML blog and a domain name and wants their posts to be read by others and get replies. Instead of setting up a CMS, they decide to set up an ActivityPub server.

Single-Server Social Feeds

An exercise app wants to build in social features to make their users feel proud of their achievements. This would probably include something like a notification bell and a feed where updates about their friends appear. Users might have with the option to react with an emoji or sticker.

All these exchanges would stay local to the server.

Private Group Chat

A small group of people who communicate online become dissatisfied with their existing app's policies and decide to communicate privately. They would like to develop their own system for communication.

Although ActivityPub does not define an encryption layer, messages could stay local to a single server or could be exchanged between all parties in an ephermeral way. Encryption could be a good addition, however.

Federated Social Network

Ideally this project could be used to build an entire social network that interfaces with the rest of the Fediverse.

activity-kit's People

Contributors

gobengo avatar michaelcpuckett avatar neothethird avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

activity-kit's Issues

Follow/Accept flow bug

I have attempted to follow the Follow/Accept flow as specified but bugs tend to pop up during testing, such as:

  • After both actors follow each other, one will end up with an additional follower (the same follower, twice)

type is to restrictive (?)

Hello,

all the things which are not marked functional in the spec. can have multiple items.
So can type.
In fact if you look at the context, type is just an alias for the @type of JSON-LD.
It is not restricted to any Vocabulary.
For example
"type": ["Collection", "redaktor:PressFreedom"] or
"type": ["Place", "schema:Country"] or from my Public Broadcaster an Array with 8 types ;)

So, wondered about
type: typeof CoreObjectTypes[keyof typeof CoreObjectTypes];

Avoid hanging on uncaught errors

Per @gobengo:

activitypub-core-server-express should probably catch unexpected exceptions in the endpoint respond methods and pass the error to express's next(err) callback. without that, express will hang on uncaught errors because res.end() is never called

Groups

Groups are mostly outside the ActivityPub spec, but my understanding is that some implementations do something akin to the following flow:

  • Person Actor "Alice" follows Group Actor "Star Wars Fans"
  • Group Actor "Star Wars Fans" accepts the follow, adding "Alice" to its Followers collection
  • "Alice" broadcasts a Note Object wrapped in a Create Activity and addresses it to "Star Wars Fans"
  • "Star Wars Fans" receives the Create Activity and, as a side effect, broadcasts an Announce Activity referencing the Note Object, addressed to its Following collection. This is only if the Actor of the Create Activity is in the Group Actor's Followers collection.

Clients may interpret this in various ways, but I assume for most users this just literally shows the Group Actor announcement/share/boost/etc. alongside the rest of the timeline. I think ideally a client would isolate Group Announce Activities into a separate area.

Do you want an AWS DynamoDB adapter?

Hi maintainers,

I am interested in contributing a new feature to your project. I have an idea for a DynamoDB database adapter and wanted to get your feedback before I start working on it.

Is this something that would be useful for the project? Are there any guidelines or preferences I should follow while implementing it? (e.g. on what branch should I submit a pull request, etc.)

Looking forward to your response.

Thank you,
Matthias

(outbox:upload-media) Accepts Uploaded Media in submissions [MUST]

https://w3c.github.io/activitypub/#uploading-media

Servers MAY support uploading document types to be referenced in activites, such as images, video or other binary data, but the precise mechanism is out of scope for this version of ActivityPub. The Social Web Community Group is refining the protocol in the ActivityPub Media Upload report.

https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload

The Media Upload mechanism defines a protocol for ActivityPub servers to support uploading document types to be referenced in activites, such as images, video or other binary data. To accomplish this, a client MUST submit a multipart/form-data message to the user's uploadMedia endpoint on their ActivityStreams profile object. (Unlike most client to server interactions, using this endpoint does not involve submitting to the outbox). A client should expect that it must be properly authenticated in order to be able to upload media.

The uploadMedia endpoint is part of the ActivityPub endpoints mapping. See ActivityPub extensions.

The submitted form data should contain two parts / fields:

file: The media file file being uploaded.
object: A shell of an ActivityStreams object, which will be finalized by the server.

Assuming that the server accepts the request and that the user was appropriately authorized to upload media, servers MUST respond with a 201 Created if the object is immediately available or a 202 Accepted if the server is still processing the submitted media. The response MUST contain a Location header pointing to the new or to-be-created object's id.

The server, having done any appropriate processing on the received file and putting it in place, transforms the object that will be retrievable by the id. In particular, servers MUST append an id property to the object, and SHOULD include the uploaded and/or processed file paths in the object's url property. The server MAY wrap the shell object submitted by the user in a Create via (object-without-create) if appropriate and present this as the object pointed to by the forementioned Location header in the post-media-upload response.

announcing group messages to the whole fediverse is spammy

As far as I understand, the groups plugin sends Announces to all known inboxes:

https://github.com/michaelcpuckett/activitypub-core/blob/34d57d2d7f89bf48da698116273dc86240f2f088/packages/activitypub-core-plugin-groups/src/index.ts#L212

I was wondering why some posts where showing up on my single user instance's global timeline when I noticed that they were Announced by an actor from chirp.social. My general expectation is that activities are only sent to following actors, otherwise this is sending bulk unsolicited content.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.