Coder Social home page Coder Social logo

dahlia / fedify Goto Github PK

View Code? Open in Web Editor NEW
177.0 4.0 6.0 1.82 MB

ActivityPub server framework in TypeScript

Home Page: https://fedify.dev/

License: GNU Affero General Public License v3.0

TypeScript 100.00%
activitypub deno fediverse typescript bun nodejs

fedify's Introduction

Fedify: an ActivityPub server framework

JSR npm GitHub Actions Matrix Follow @hongminhee@todon.eu

Note

Looking for a quick demo? Here it is: Fedify Demo on Deno Playground.

Fedify is a TypeScript library for building federated server apps powered by ActivityPub and other standards, so-called fediverse.1 It aims to eliminate the complexity and redundant boilerplate code when building a federated server app, so that you can focus on your business logic and user experience.

Currently, Fedify is moving fast and the API is not stable yet. We do not recommend using it in production yet, so please use it if you would like to experiment with it and help us improve it.

The rough roadmap is to implement the following features out of the box:

If you want to know more about the project, please take a look at the following resources:

If you have any questions, suggestions, or feedback, please feel free to join our Matrix chat space or GitHub Discussions.

Installation

Fedify is available on JSR for Deno and on npm for Node.js and Bun. Although Fedify can be used in Node.js and Bun, it's primarily designed for Deno. We recommend using Deno for the best experience, but you can use Node.js or Bun if you prefer.

Tip

If you are new to Deno, but already familiar with Node.js, you can think of Deno as a more modern version of Node.js created by the same person, Ryan Dahl. Deno has a lot of improvements over Node.js, such as better security, better TypeScript support, better ES module support, and built-in key-value store and message queue.

Deno

Deno is the primary runtime for Fedify. As a prerequisite, you need to have Deno 1.41.0 or later installed on your system. Then you can install Fedify via the following command:

deno add @fedify/fedify
import { Federation } from "@fedify/fedify";

Or you can directly import it in your code using jsr: specifier:

import { Federation } from "jsr:@fedify/fedify";

Node.js

Fedify can also be used in Node.js. As a prerequisite, you need to have Node.js 20.0.0 or later installed on your system. Then you can install Fedify via the following command:

npm add @fedify/fedify
import { Federation } from "@fedify/fedify";

Bun

Fedify can also be used in Bun. You can install it via the following command:

bun add @fedify/fedify
import { Federation } from "@fedify/fedify";

Footnotes

  1. You may already know some of the networks in the fediverse, such as Mastodon, Lemmy, Pixelfed, PeerTube, and so on. โ†ฉ

fedify's People

Contributors

dahlia avatar wakest avatar zach-planet-nine 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  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

fedify's Issues

Outbox auto-filler

We can store all outgoing activities in the Deno KV via sendActivity() so that the outbox can be populated with items without the need for an outbox dispatcher.

new Federation<void>({
  autoOutboxDispatcher: true
});

However, it's necessary to check the visibility of activities and hide them if they're not public. (We can add authentication later.)

Vocabulary API: document loader inheritance

Currently, to properly apply remote document loading strategy, we need to explicitly specify a document loader every time. It would be more ergonomic if the document loader is inherited, e.g.:

const object = undo.getObject(ctx);
assert(object instanceof Follow);
const actor = object.getActor(); // loaded using ctx

Docs on pragmatics

While Fedify provides a vocabulary API, it does not provide detailed docs on how to utilize those vocabularies. However, ActivityPub implementations like Mastodon and Misskey already have de facto norms for how to use them, which you should follow to get the desired results.

For example, you need to know which properties on a Person object should be populated with which values to display an avatar or header image, which property represents a date joined, and so on.

We need to have a dedicated reference for this kind of usage of vocabulary.

Custom WebFinger query

Currently, there are a limited number of resources that can be queried with WebFinger, but we need to make them customizable.

Public Key Response Handling And Just Overall Thoughts

First let me start by saying great job on this repo. Having spent the last two days with it and ActivityPub in general, I'm impressed with how much work is here, and what it's capable of given how convoluted ActivityPub is (not a knock on the protocol, anything that's trying to cover a big use case is going to be hard to grok).

I've got my server servin', and am able to send a Follow activity to a user on a Mastodon instance. That generates a GET request back to my server to get the actor's publicKey, and for the life of me I can't figure out what I'm supposed to send back. I have the following:

const jsonLd = {
                "@context": "https://www.w3.org/ns/activitystreams",
                "id": new URL("https://definitely-quality-amoeba.ngrok-free.app/users/planet-nine-test#main-key"),
                type: "Person",
                "publicKey": {
                    "id": new URL("https://definitely-quality-amoeba.ngrok-free.app/users/planet-nine-test#main-key"),
                    "owner": "https://definitely-quality-amoeba.ngrok-free.app/users/planet-nine-test",
                    publicKeyPem: pemPublicKey
                }
        };
        return new Response(JSON.stringify(jsonLd));

but still get the error from Mastodon that Unable to fetch key JSON at https://definitely-quality-amoeba.ngrok-free.app/users/planet-nine-test#main-key.

Figuring out what this should would help me in the short term, but in the long term I think this should be abstracted more away from the implementer. I think that's your intent with the federation callbacks, but it looks like maybe they're only partially implemented? There's setKeyPairDispatcher, but as far as I can tell, the only place it's called is in getKeyPairFromHandle in the context middleware, and when invoked it doesn't even return the publicKey.

Which brings me to my larger thought. You're sort of sitting in between ease of use, and extensibility right now, and I think you should pick one and go more in that direction. Since your goal for this repo is: "It aims to eliminate the complexity and redundant boilerplate code when building a federated server app" I'd pick the former. Fedify should just handle key requests for me imo. In fact I'd go so far as to say Fedify should just handle all the crypto stuff without me having to think about it since letting people do their own crypto stuff is dangerous.

If you wanted to go the other way, I'd suggest losing the dependence on DenoKV, and thus Deno itself, and build server bindings, and DB bindings.

Anyway, just my 2 cents. If I can figure out the publicKey response I'll do another PR for what I have. My plan is to build a minimal implementation for all the Fediverse types (micro-blog, friendster, music, etc) so people can see how to follow and post to them via Fedify. My plan is to use https://github.com/planet-nine-app/sessionless, which is an identity system I've been working on so that people don't have to deal with email/password.

Again nice work on this. It's been very helpful in getting started.

Blog example cannot be built because KV connection is listening indefinitely

Building the blog example is broken because the process is kept open indefinitely so the build task never finishes. Reason for that is that a Deno KV instance is opened and a listener is attached eagerly. This will prevent the process from exiting.

The kv queue is eagerly listened through this call here:

this.#kv.listenQueue(handler);
which is ultimately kicked off eagerly in the constructor of the Federation class here: https://github.com/dahlia/fedify/blob/main/federation/middleware.ts#L197

Commenting this out makes the build task finish as expected.

Steps to reproduce

  1. Go into the examples blog folder: cd examples/blog
  2. Run deno task build -> Process never exists

Support for attachment lists on actors (Mastodon profile fields)

Mastodon uses the attachment property on actors for a list of profile fields, documented here. This property is pretty widely adopted by other ActivityPub-based social platforms.

It would be helpful if Fedify could provide a way to specify these profile fields for its actors. If I understand it correctly, it is not currently possible to add this property manually while using Fedify for federation.

Note: The Mastodon web GUI limits users to a maximum of four profile fields, but to my knowledge the number of fields that federated profiles may have is not limited.

Tutorial

Needs a tutorial that users can follow step-by-step.

Deployment docs

The docs should cover how to deploy a federated server app.

Using the Fresh integration, visiting a non-existing actor in the browser produces a short 406 response instead of showing Fresh's 404 error page

Using Fedify and Fresh on Deno, all up to date versions. Whenever HTML is requested, the Federation object should let Fresh render the output, including error pages.

If I request a URI in the browser (or via curl with Accept: text/html) whose Fresh route returns a 404 error, and the URL maps to the registered actor dispatcher, the Federation handler appears to return a plain text 406 error in place of Fresh's 404 page. This should not happen.

Fresh middleware:

export const handler = integrateHandler(
  federation,
  (req, ctx) => undefined, // context data
);

Fresh handler:

export default async function UserPage(req: Request, ctx: RouteContext) {
  return ctx.renderNotFound();
}

Actor dispatcher:

federation.setActorDispatcher("/{handle}", async (ctx, handle, key) => {
  console.log("Invoked for:", handle);
  return null;
}

Testing shows that if application/activity+json is requested, the actor dispatcher is correctly invoked, returns null, and the client receives the HTML error page. If text/html is requested, the actor dispatcher is apparently not invoked, and the client receives a plain 406 Not acceptable error.

I hope the source of the problem is not some other part of my project, but this is the issue I am seeing.

/.well-known/nodeinfo

According to NodeInfo repository:

NodeInfo is an effort to create a standardized way of exposing metadata about a server running one of the distributed social networks. The two key goals are being able to get better insights into the user base of distributed social networking and the ability to build tools that allow users to choose the best-fitting software and server for their needs.

FetchError 401 requesting mastodon v4.2.8 user

Blog example throws an error:

FetchError: https://indieweb.social/users/thomasreggi: HTTP 401: https://indieweb.social/users/thomasreggi
    at getRemoteDocument (https://jsr.io/@fedify/fedify/0.7.0-dev.120+4c00fc5e/runtime/docloader.ts:73:11)
    at fetchDocumentLoader (https://jsr.io/@fedify/fedify/0.7.0-dev.120+4c00fc5e/runtime/docloader.ts:94:10)
    at eventLoopTick (ext:core/01_core.js:168:7)
    at async https://jsr.io/@fedify/fedify/0.7.0-dev.120+4c00fc5e/runtime/docloader.ts:203:25
    at async verify (https://jsr.io/@fedify/fedify/0.7.0-dev.120+4c00fc5e/httpsig/mod.ts:154:24)
    at async handleInbox (https://jsr.io/@fedify/fedify/0.7.0-dev.120+4c00fc5e/federation/handler.ts:251:15)
    at async Federation.fetch (https://jsr.io/@fedify/fedify/0.7.0-dev.120+4c00fc5e/federation/middleware.ts:882:16)
    at async https://jsr.io/@fedify/fedify/0.7.0-dev.120+4c00fc5e/x/fresh.ts:117:12
    at async handler (https://deno.land/x/[email protected]/src/server/context.ts:293:14)
    at async ext:deno_http/00_serve.js:455:18 {
  url: URL {
    href: "https://indieweb.social/users/thomasreggi",
    origin: "https://indieweb.social",
    protocol: "https:",
    username: "",
    password: "",
    host: "indieweb.social",
    hostname: "indieweb.social",
    port: "",
    pathname: "/users/thomasreggi",
    hash: "",
    search: ""
  },
  name: "FetchError"
}

from a mastodon server indeweb.social running

{
  "version": "2.0",
  "software": {
    "name": "mastodon",
    "version": "4.2.8"
  },
  "protocols": [
    "activitypub"
  ],
  "services": {
    "outbound": [],
    "inbound": []
  },
  "usage": {
    "users": {
      "total": 11387,
      "activeMonth": 1373,
      "activeHalfyear": 2298
    },
    "localPosts": 506791
  },
  "openRegistrations": true,
  "metadata": {

  }
}

Authenticated document loader

Currently the built-in document loaders do not authenticate. In order to fully access ActivityPub networks, we should provide the way to load authorized remote documents.

The API could look like below:

interface Context<TContextData> {
  getDocumentLoader(handle: string): DocumentLoader | undefined;
}

Add totalItems property to Collection objects

The totalItems property contains an integer expressing the total number of items in the collection, specified in ActivityPub here. In my brief tests returning an array of URLs from an OutboxDispatcher, Fedify 0.5.0 did not add this property to the OrderedCollection object. Per the spec, it is applicable to all Collections.

In terms of interoperability, adding this property would allow Mastodon and other platforms to display the total number of posts by an Actor, assuming the outbox is populated with an OrderedCollection.

Change organizeImports to sortImports in .vscode

I'm embarrassed to say how long this took me to figure out, but here's the problem. I was working on an example to contribute (I'm the guy on Reddit who's been trying to figure out your repo here), and because I'm old and paranoid, I save my files often. Sometimes the saving would delete imports I had added and sometimes not, and I couldn't figure out why.

Turns out it's because of organizeImports in your .vscode settings. That led me to this thread: microsoft/TypeScript#36085, which suggests replacing organizeImports with sortImports. Since unused imports are caught by the linter (though not in the examples dir which is excluded currently), the organizeImports setting is redundant for them, and just causes frustration for anyone trying to contribute.

It's a one line change, which I'm happy to submit a PR for if you add me to the repo.

Getting error trying to run actor cli example

Getting the following error when trying to run the actor cli example:

error: Module not found "file:///Users/name/Work/planet-nine/third-party/fedify/fedify/vocab/vocab.ts".
    at file:///Users/name/Work/planet-nine/third-party/fedify/fedify/vocab/mod.ts:54:15

If I get more time I might try to debug.

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.