Skip to main content

Built and signed on GitHub Actions

Federation types, validators and cryptography for Versia server implementations.

This package works with Cloudflare Workers, Node.js, Deno, Bun, Browsers
This package works with Cloudflare Workers
This package works with Node.js
This package works with Deno
This package works with Bun
This package works with Browsers
JSR Score
52%
Published
a month ago (0.1.4)

Versia Logo

@versia/federation

Federation types, validators and cryptography for Versia server implementations.

Efficiency

The built output of the package is not even 200 KB in size, making it a lightweight and efficient solution for your Versia needs. Installing the package adds around 5 MB to your node_modules folder, but this does not affect the final bundle size.

Compilation (bundling/minifying) time is a few seconds, almost all of which is spent on type-checking. The actual compilation time is less than a tenth of a second.

Usage

Federation

Validation

Zod is used to validate and parse the objects. All Versia entities are already written for you.

import { EntityValidator, type ValidationError } from "@versia/federation";

const validator = new EntityValidator();

try {
    // Will throw an error when the object is invalid, otherwise return the correct object
    const invalidNote = await validator.Note({
        // This is invalid
        type: "Note",
        content: 123,
    });
} catch (error) {
    // ToString returns the human-friendly error message
    sendUser((error as ValidationError).toString());
}

// Types are also included for TypeScript users that don't use the extracted ones
import type { Note } from "@versia/federation/types";

const validNoteObject: Note = {
    type: "Note",
    // ...
};

const validNote = await validator.Note(validNoteObject);

// validNote is still the same as noteObject

Your editor's IntelliSense should provide you with every method and property available, which all match the Versia specification names.

Requester

A FederationRequester class is provided to make requests to a remote server. It sets the correct headers and has multiple methods to make requesters easier.

import { FederationRequester, SignatureConstructor } from "@versia/federation";

const requester = new FederationRequester(
    new URL("https://example.com"),
    new SignatureConstructor(privateKey, keyId),
);

const { data, ok } = await requester.get<User>("/users/1");

if (!ok) {
    console.error(data);
}

console.log(data);

// Do a WebFinger request
const userProfileUri = await requester.webFinger("banana");

Validation Helper

RequestParserHandler is a class to parse the body of a request and call the appropriate callback. It is a helper for the EntityValidator class.

const body = { ... };
const validator = new EntityValidator();
const parser = new RequestParserHandler(body, validator);

// Throws an error if the object is invalid
// Same error as above
await parser.parseBody({
    note: (note) => {
        // If the object is a Note, this will be called
        console.log(note);
    },
    follow: (follow) => {
        // If the object is a Follow, this will be called
        console.log(follow);
    },
    ...
});

Cryptography

The cryptography module provides two main classes: SignatureConstructor and SignatureValidator. These classes are used to construct and validate signatures for requests, respectively.

SignatureConstructor

The SignatureConstructor class is used to construct a signature for a request. It can be instantiated with a private key and a key ID:

const privateKey = // CryptoKey
const keyId = "https://example.com/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51";
const constructor = new SignatureConstructor(privateKey, keyId);

Alternatively, you can create a SignatureConstructor instance from a base64-encoded private key:

const privateKey = "base64PrivateKey";
const keyId = "https://example.com/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51";
const constructor = await SignatureConstructor.fromStringKey(privateKey, keyId);

The sign method is used to sign a request:

const request = new Request();
// Returns a Request object with Signature and Date set
const signature = await constructor.sign(request);
// Alternatively
// Returns a Header object with Signature and Date set
const signature = await constructor.sign(date, method, url, body);
SignatureValidator

The SignatureValidator class is used to validate the signature of a request. It can be instantiated with a public key:

const publicKey = // CryptoKey
const validator = new SignatureValidator(publicKey);

Alternatively, you can create a SignatureValidator instance from a base64-encoded public key:

const publicKey = "base64PublicKey";
const validator = await SignatureValidator.fromStringKey(publicKey);

The validate method is used to validate a request or signature:

const request = new Request();
// Returns boolean or TypeError if data is invalid
const isValid = await validator.validate(request);
// Alternatively
// Returns boolean or TypeError if data is invalid
const isValid = await validator.validate(signature, date, method, url, body);

Please note that these classes require the WebCrypto API and Ed25519 support in the environment. WebCrypto support is automatically checked, but Ed25519 cannot be as far as I know.

Getting Started

Prerequisites

For Usage

See the Compatibility section for the supported environments. Any package manager can be used to install the packages.

For Development

  • Bun version 1.1.8 or higher.
  • Either the Linux or macOS operating systems. (Windows will work, but is not officially supported.)

Compatibility

This library is built for JavaScript runtimes with the support for:

Runtimes

  • Node.js: 14.0+ is the minimum (18.0+ for cryptography), but only Node.js 20.0+ (LTS) is officially supported.
  • Deno: Support is unknown. 1.0+ is expected to work.
  • Bun: Bun 1.1.8 is the minimum-supported version. As Bun is rapidly evolving, this may change. Previous versions may also work.

Browsers

Consequently, this library is compatible without any bundling in the following browser versions:

  • Chrome: 80+
  • Edge: 80+
  • Firefox: 74+
  • Safari: 13.1+
  • Opera: 67+
  • Internet Explorer: None

Cryptography functions are supported in the following browsers:

  • Safari: 17.0+
  • Chrome: 113.0+ with #enable-experimental-web-platform-features enabled

If you are targeting older browsers, please don't, you are doing yourself a disservice.

Transpilation to non-ES Module environments is not officially supported, but should be simple with the use of a bundler like Parcel or Rollup.

Installation

Package is distributed as a scoped package on the NPM registry and JSR.

We strongly recommend using JSR over NPM for all your packages that are available on it.

# NPM version
deno add npm:@versia/federation # For Deno
npm install @versia/federation # For NPM
yarn add @versia/federation # For Yarn
pnpm add @versia/federation # For PNPM
bun add @versia/federation # For Bun

# JSR version
deno add @versia/federation # For Deno
npx jsr add @versia/federation # For JSR
yarn dlx jsr add @versia/federation # For Yarn
pnpm dlx jsr add @versia/federation # For PNPM
bunx jsr add @versia/federation # For Bun

From Source

If you want to install from source, you can clone this repository and run the following commands:

bun install # Install dependencies

bun run build # Build the packages

The built package will be in the federation/dist folder.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

Projects

  • Bun: Thanks to the Bun team for creating an amazing JavaScript runtime.
  • TypeScript: TypeScript is the backbone of this project.
  • Node.js: Node.js created the idea of JavaScript on the server.

People

  • April John: Creator and maintainer of the Versia Server ActivityPub bridge.
Built and signed on
GitHub Actions
View transparency log

Add Package

deno add jsr:@versia/federation

Import symbol

import * as federation from "@versia/federation";

---- OR ----

Import directly with a jsr specifier

import * as federation from "jsr:@versia/federation";

Add Package

npx jsr add @versia/federation

Import symbol

import * as federation from "@versia/federation";

Add Package

yarn dlx jsr add @versia/federation

Import symbol

import * as federation from "@versia/federation";

Add Package

pnpm dlx jsr add @versia/federation

Import symbol

import * as federation from "@versia/federation";

Add Package

bunx jsr add @versia/federation

Import symbol

import * as federation from "@versia/federation";