@fedify/relay 2.0.0-dev.12 → 2.0.0-dev.150
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +149 -43
- package/dist/mod.cjs +385 -386
- package/dist/mod.d.cts +122 -30
- package/dist/mod.d.ts +123 -30
- package/dist/mod.js +383 -384
- package/package.json +10 -7
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -13,6 +13,9 @@ This package provides ActivityPub relay functionality for the [Fedify]
|
|
|
13
13
|
ecosystem, enabling the creation and management of relay servers that can
|
|
14
14
|
forward activities between federated instances.
|
|
15
15
|
|
|
16
|
+
For comprehensive documentation on building and operating relay servers,
|
|
17
|
+
see the [*Relay server* section in the Fedify manual][manual].
|
|
18
|
+
|
|
16
19
|
|
|
17
20
|
What is an ActivityPub relay?
|
|
18
21
|
------------------------------
|
|
@@ -33,7 +36,7 @@ This package supports two popular relay protocols used in the fediverse:
|
|
|
33
36
|
### Mastodon-style relay
|
|
34
37
|
|
|
35
38
|
The Mastodon-style relay protocol uses LD signatures for activity
|
|
36
|
-
verification and follows the Public collection.
|
|
39
|
+
verification and follows the Public collection. This protocol is widely
|
|
37
40
|
supported by Mastodon and many other ActivityPub implementations.
|
|
38
41
|
|
|
39
42
|
Key features:
|
|
@@ -46,11 +49,16 @@ Key features:
|
|
|
46
49
|
|
|
47
50
|
### LitePub-style relay
|
|
48
51
|
|
|
49
|
-
*LitePub relay support is planned for a future release.*
|
|
50
|
-
|
|
51
52
|
The LitePub-style relay protocol uses bidirectional following relationships
|
|
52
53
|
and wraps activities in `Announce` activities for distribution.
|
|
53
54
|
|
|
55
|
+
Key features:
|
|
56
|
+
|
|
57
|
+
- Reciprocal following between relay and subscribers
|
|
58
|
+
- Activities wrapped in `Announce` for distribution
|
|
59
|
+
- Two-phase subscription (pending → accepted)
|
|
60
|
+
- Enhanced federation capabilities
|
|
61
|
+
|
|
54
62
|
|
|
55
63
|
Installation
|
|
56
64
|
------------
|
|
@@ -83,46 +91,101 @@ bun add @fedify/relay
|
|
|
83
91
|
Usage
|
|
84
92
|
-----
|
|
85
93
|
|
|
86
|
-
### Creating a
|
|
94
|
+
### Creating a relay
|
|
87
95
|
|
|
88
|
-
Here's a simple example of creating a
|
|
96
|
+
Here's a simple example of creating a relay server using the factory function:
|
|
89
97
|
|
|
90
98
|
~~~~ typescript
|
|
91
|
-
import {
|
|
99
|
+
import { createRelay } from "@fedify/relay";
|
|
92
100
|
import { MemoryKvStore } from "@fedify/fedify";
|
|
93
101
|
|
|
94
|
-
|
|
102
|
+
// Create a Mastodon-style relay
|
|
103
|
+
const relay = createRelay("mastodon", {
|
|
95
104
|
kv: new MemoryKvStore(),
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
origin: "https://relay.example.com",
|
|
106
|
+
// Required: Set a subscription handler to approve/reject subscriptions
|
|
107
|
+
subscriptionHandler: async (ctx, actor) => {
|
|
108
|
+
// For an open relay, simply return true
|
|
109
|
+
// return true;
|
|
110
|
+
|
|
111
|
+
// Or implement custom approval logic:
|
|
112
|
+
const domain = new URL(actor.id!).hostname;
|
|
113
|
+
const blockedDomains = ["spam.example", "blocked.example"];
|
|
114
|
+
return !blockedDomains.includes(domain);
|
|
115
|
+
},
|
|
106
116
|
});
|
|
107
117
|
|
|
108
118
|
// Serve the relay
|
|
109
119
|
Deno.serve((request) => relay.fetch(request));
|
|
110
120
|
~~~~
|
|
111
121
|
|
|
122
|
+
You can also create a LitePub-style relay by changing the type:
|
|
123
|
+
|
|
124
|
+
~~~~ typescript
|
|
125
|
+
const relay = createRelay("litepub", {
|
|
126
|
+
kv: new MemoryKvStore(),
|
|
127
|
+
origin: "https://relay.example.com",
|
|
128
|
+
subscriptionHandler: async (ctx, actor) => true,
|
|
129
|
+
});
|
|
130
|
+
~~~~
|
|
131
|
+
|
|
112
132
|
### Subscription handling
|
|
113
133
|
|
|
114
|
-
|
|
115
|
-
|
|
134
|
+
The `subscriptionHandler` is required and determines whether to approve or reject
|
|
135
|
+
subscription requests. For an open relay that accepts all subscriptions:
|
|
116
136
|
|
|
117
137
|
~~~~ typescript
|
|
118
|
-
relay
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return allowedDomains.includes(domain);
|
|
138
|
+
const relay = createRelay("mastodon", {
|
|
139
|
+
kv: new MemoryKvStore(),
|
|
140
|
+
origin: "https://relay.example.com",
|
|
141
|
+
subscriptionHandler: async (ctx, actor) => true, // Accept all
|
|
123
142
|
});
|
|
124
143
|
~~~~
|
|
125
144
|
|
|
145
|
+
You can also implement custom approval logic:
|
|
146
|
+
|
|
147
|
+
~~~~ typescript
|
|
148
|
+
const relay = createRelay("mastodon", {
|
|
149
|
+
kv: new MemoryKvStore(),
|
|
150
|
+
origin: "https://relay.example.com",
|
|
151
|
+
subscriptionHandler: async (ctx, actor) => {
|
|
152
|
+
// Example: Only allow subscriptions from specific domains
|
|
153
|
+
const domain = new URL(actor.id!).hostname;
|
|
154
|
+
const allowedDomains = ["mastodon.social", "fosstodon.org"];
|
|
155
|
+
return allowedDomains.includes(domain);
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
~~~~
|
|
159
|
+
|
|
160
|
+
### Managing followers
|
|
161
|
+
|
|
162
|
+
The relay provides methods to query and manage followers without exposing
|
|
163
|
+
internal storage details.
|
|
164
|
+
|
|
165
|
+
#### Listing all followers
|
|
166
|
+
|
|
167
|
+
~~~~ typescript
|
|
168
|
+
for await (const follower of relay.listFollowers()) {
|
|
169
|
+
console.log(`Follower: ${follower.actorId}`);
|
|
170
|
+
console.log(`State: ${follower.state}`);
|
|
171
|
+
console.log(`Actor name: ${follower.actor.name}`);
|
|
172
|
+
console.log(`Actor type: ${follower.actor.constructor.name}`);
|
|
173
|
+
}
|
|
174
|
+
~~~~
|
|
175
|
+
|
|
176
|
+
#### Getting a specific follower
|
|
177
|
+
|
|
178
|
+
~~~~ typescript
|
|
179
|
+
const follower = await relay.getFollower("https://mastodon.example.com/users/alice");
|
|
180
|
+
if (follower) {
|
|
181
|
+
console.log(`Found follower in state: ${follower.state}`);
|
|
182
|
+
console.log(`Actor username: ${follower.actor.preferredUsername}`);
|
|
183
|
+
console.log(`Inbox: ${follower.actor.inboxId?.href}`);
|
|
184
|
+
} else {
|
|
185
|
+
console.log("Follower not found");
|
|
186
|
+
}
|
|
187
|
+
~~~~
|
|
188
|
+
|
|
126
189
|
### Integration with web frameworks
|
|
127
190
|
|
|
128
191
|
The relay's `fetch()` method returns a standard `Response` object, making it
|
|
@@ -131,13 +194,14 @@ example with Hono:
|
|
|
131
194
|
|
|
132
195
|
~~~~ typescript
|
|
133
196
|
import { Hono } from "hono";
|
|
134
|
-
import {
|
|
197
|
+
import { createRelay } from "@fedify/relay";
|
|
135
198
|
import { MemoryKvStore } from "@fedify/fedify";
|
|
136
199
|
|
|
137
200
|
const app = new Hono();
|
|
138
|
-
const relay =
|
|
201
|
+
const relay = createRelay("mastodon", {
|
|
139
202
|
kv: new MemoryKvStore(),
|
|
140
|
-
|
|
203
|
+
origin: "https://relay.example.com",
|
|
204
|
+
subscriptionHandler: async (ctx, actor) => true,
|
|
141
205
|
});
|
|
142
206
|
|
|
143
207
|
app.use("*", async (c) => {
|
|
@@ -191,38 +255,61 @@ details.
|
|
|
191
255
|
API reference
|
|
192
256
|
-------------
|
|
193
257
|
|
|
194
|
-
### `
|
|
195
|
-
|
|
196
|
-
A Mastodon-compatible ActivityPub relay implementation.
|
|
258
|
+
### `createRelay()`
|
|
197
259
|
|
|
198
|
-
|
|
260
|
+
Factory function to create a relay instance.
|
|
199
261
|
|
|
200
262
|
~~~~ typescript
|
|
201
|
-
|
|
263
|
+
function createRelay(
|
|
264
|
+
type: "mastodon" | "litepub",
|
|
265
|
+
options: RelayOptions
|
|
266
|
+
): Relay
|
|
202
267
|
~~~~
|
|
203
268
|
|
|
204
|
-
|
|
269
|
+
**Parameters:**
|
|
270
|
+
|
|
271
|
+
- `type`: The type of relay to create (`"mastodon"` or `"litepub"`)
|
|
272
|
+
- `options`: Configuration options for the relay
|
|
273
|
+
|
|
274
|
+
**Returns:** A `Relay` instance
|
|
205
275
|
|
|
206
|
-
|
|
276
|
+
### `Relay`
|
|
277
|
+
|
|
278
|
+
Public interface for ActivityPub relay implementations.
|
|
207
279
|
|
|
208
280
|
#### Methods
|
|
209
281
|
|
|
210
282
|
- `fetch(request: Request): Promise<Response>`: Handle incoming HTTP requests
|
|
211
|
-
- `
|
|
212
|
-
|
|
283
|
+
- `listFollowers(): AsyncIterableIterator<RelayFollower>`: Lists all
|
|
284
|
+
followers of the relay
|
|
285
|
+
- `getFollower(actorId: string): Promise<RelayFollower | null>`: Gets
|
|
286
|
+
a specific follower by actor ID
|
|
287
|
+
- `getActorUri(): Promise<URL>`: Gets the URI of the relay actor
|
|
288
|
+
- `getSharedInboxUri(): Promise<URL>`: Gets the shared inbox URI of the relay
|
|
289
|
+
|
|
290
|
+
#### Relay types
|
|
291
|
+
|
|
292
|
+
The relay type is specified when calling `createRelay()`:
|
|
293
|
+
|
|
294
|
+
- `"mastodon"`: Mastodon-compatible relay using direct activity forwarding,
|
|
295
|
+
immediate subscription approval, and LD signatures
|
|
296
|
+
- `"litepub"`: LitePub-compatible relay using bidirectional following,
|
|
297
|
+
activities wrapped in `Announce`, and two-phase subscription
|
|
213
298
|
|
|
214
299
|
### `RelayOptions`
|
|
215
300
|
|
|
216
301
|
Configuration options for the relay:
|
|
217
302
|
|
|
218
303
|
- `kv: KvStore` (required): Key–value store for persisting relay data
|
|
219
|
-
- `
|
|
304
|
+
- `origin: string` (required): Relay's origin URL (e.g.,
|
|
305
|
+
`"https://relay.example.com"`)
|
|
306
|
+
- `name?: string`: Relay's display name (defaults to `"ActivityPub Relay"`)
|
|
307
|
+
- `subscriptionHandler: SubscriptionRequestHandler` (required): Handler for
|
|
308
|
+
subscription approval/rejection
|
|
220
309
|
- `documentLoaderFactory?: DocumentLoaderFactory`: Custom document loader
|
|
221
310
|
factory
|
|
222
311
|
- `authenticatedDocumentLoaderFactory?: AuthenticatedDocumentLoaderFactory`:
|
|
223
312
|
Custom authenticated document loader factory
|
|
224
|
-
- `federation?: Federation<void>`: Custom Federation instance (for advanced
|
|
225
|
-
use cases)
|
|
226
313
|
- `queue?: MessageQueue`: Message queue for background activity processing
|
|
227
314
|
|
|
228
315
|
### `SubscriptionRequestHandler`
|
|
@@ -231,21 +318,39 @@ A function that determines whether to approve a subscription request:
|
|
|
231
318
|
|
|
232
319
|
~~~~ typescript
|
|
233
320
|
type SubscriptionRequestHandler = (
|
|
234
|
-
ctx: Context<
|
|
321
|
+
ctx: Context<RelayOptions>,
|
|
235
322
|
clientActor: Actor,
|
|
236
323
|
) => Promise<boolean>
|
|
237
324
|
~~~~
|
|
238
325
|
|
|
239
|
-
Parameters
|
|
326
|
+
**Parameters:**
|
|
240
327
|
|
|
241
|
-
- `ctx`: The Fedify context object
|
|
328
|
+
- `ctx`: The Fedify context object with relay options
|
|
242
329
|
- `clientActor`: The actor requesting to subscribe
|
|
243
330
|
|
|
244
|
-
Returns
|
|
331
|
+
**Returns:**
|
|
245
332
|
|
|
246
333
|
- `true` to approve the subscription
|
|
247
334
|
- `false` to reject the subscription
|
|
248
335
|
|
|
336
|
+
### `RelayFollower`
|
|
337
|
+
|
|
338
|
+
A follower of the relay with validated Actor instance:
|
|
339
|
+
|
|
340
|
+
~~~~ typescript
|
|
341
|
+
interface RelayFollower {
|
|
342
|
+
readonly actorId: string;
|
|
343
|
+
readonly actor: Actor;
|
|
344
|
+
readonly state: "pending" | "accepted";
|
|
345
|
+
}
|
|
346
|
+
~~~~
|
|
347
|
+
|
|
348
|
+
**Properties:**
|
|
349
|
+
|
|
350
|
+
- `actorId`: The actor ID (URL) of the follower
|
|
351
|
+
- `actor`: The validated Actor object
|
|
352
|
+
- `state`: The follower's state (`"pending"` or `"accepted"`)
|
|
353
|
+
|
|
249
354
|
|
|
250
355
|
[JSR]: https://jsr.io/@fedify/relay
|
|
251
356
|
[JSR badge]: https://jsr.io/badges/@fedify/relay
|
|
@@ -255,3 +360,4 @@ Returns:
|
|
|
255
360
|
[@fedify@hollo.social]: https://hollo.social/@fedify
|
|
256
361
|
[Fedify]: https://fedify.dev/
|
|
257
362
|
[Fedify documentation on key–value stores]: https://fedify.dev/manual/kv
|
|
363
|
+
[manual]: https://fedify.dev/manual/relay
|