Hotdry.
application-security

在 Node.js 中实现 ActivityPub 服务器:WebFinger 发现与 AS2 序列化支持 Mastodon 联邦

基于 Node.js 和 Express 构建 ActivityPub 服务器,实现 WebFinger 用户发现、AS2 数据序列化,支持 Mastodon 兼容的联邦发布和关注者同步,提供工程化参数与最佳实践。

在当今数字化时代,去中心化社交网络正成为对抗中心化平台的有效替代方案。正如 Ben Werdmuller 在其 FediForum 主题演讲中强调,“开放社交网络终于起飞”,它提供了一个安全的空间,让社区在不依赖单一公司的情况下组织和交流。ActivityPub 作为 W3C 标准协议,正是实现这种联邦化社交的核心技术。它允许不同服务器间的用户无缝互动,支持 Mastodon 等流行平台。本文聚焦于在 Node.js 环境中实现 ActivityPub 服务器,特别强调 WebFinger 发现机制和 ActivityStreams 2 (AS2) 序列化,用于联邦发布和关注者同步。通过这一实现,开发者可以构建兼容 Mastodon 网络的节点,确保内容跨实例传播,同时维护数据隐私和互操作性。

ActivityPub 的核心在于其双 API 设计:客户端到服务器 (C2S) API 用于用户操作,服务器到服务器 (S2S) API 用于联邦通信。WebFinger 协议是发现用户 Actor 的关键入口,当用户尝试关注如 user@domain.com 时,客户端会查询 domain 的 .well-known/webfinger 端点,返回 Actor 的 JSON-LD 描述,包括 outbox 和 inbox URL。这确保了跨域发现的标准化。AS2 则定义了活动数据的 JSON-LD 格式,例如 Create 活动用于发布帖子:{"@context": "https://www.w3.org/ns/activitystreams", "type": "Create", "actor": "https://domain.com/users/user","object": {"type":"Note","content":"Hello Fediverse!"}}。这些机制使服务器能处理 Mastodon 兼容的活动,确保序列化后的数据可被其他实例解析和验证。

在 Node.js 中实现这一服务器,最简单的方式是基于 Express 框架,结合 SQLite 存储用户数据和活动日志。安装依赖后,配置服务器监听端口(如 3000),并设置 HTTPS 以支持 HTTP Signatures 验证。核心端点包括 /users/{username} 返回 Actor JSON,/.well-known/webfinger 处理发现查询。使用 crypto 模块生成 RSA 密钥对,用于签名活动。示例代码如下:

const express = require('express'); const sqlite3 = require('sqlite3').verbose(); const app = express(); const db = new sqlite3.Database('activitypub.db');

// 初始化数据库表 db.run (CREATE TABLE IF NOT EXISTS actors ( name TEXT PRIMARY KEY, privkey TEXT, pubkey TEXT, actor JSON, followers JSON ));

// WebFinger 端点 app.get ('/.well-known/webfinger', (req, res) => { const resource = req.query.resource; if (resource.startsWith ('acct:')) { const [, username, domain] = resource.match (/acct:([^@]+)@(.+)/); if (domain !== process.env.DOMAIN) return res.status (404).send (); db.get ('SELECT actor FROM actors WHERE name = ?', [username], (err, row) => { if (err || !row) return res.status (404).send (); res.json ({ subject: resource, links: [{ rel: 'self', type: 'application/activity+json', href: JSON.parse (row.actor).id }] }); }); } });

// Actor 端点 app.get ('/users/:username', (req, res) => { const { username } = req.params; db.get ('SELECT actor FROM actors WHERE name = ?', [${username}@${process.env.DOMAIN}], (err, row) => { if (err || !row) return res.status(404).send(); res.json(JSON.parse(row.actor)); }); });

// Outbox 端点(简化) app.post ('/users/:username/outbox', authenticateSignature, (req, res) => { // 处理 Create 活动,广播到 followers const activity = req.body; if (activity.type === 'Create') { broadcastToFollowers (username, activity); } res.status (201).json (activity); });

app.listen(3000, () => console.log('Server running on port 3000'));

这一实现确保了 AS2 序列化的正确性,所有活动均以 JSON-LD 格式交换。联邦发布时,服务器需验证传入活动的签名(使用 http-signature 库),然后将 Create 活动 POST 到每个 follower 的 inbox URL。关注者同步依赖 Follow 活动处理:接收 Follow 时,回复 Accept 活动,并更新本地 followers 列表(JSON 数组存储 URL)。

为确保可靠的联邦操作,设置关键参数:签名密钥长度至少 2048 位,超时阈值 30 秒(使用 axios 配置),重试机制 3 次(指数退避)。监控点包括:活动验证失败率(<1%),inbox 交付延迟(<5s),followers 列表一致性(定期校验)。回滚策略:若同步失败,缓存活动至队列(如 Redis),离线重发。风险包括签名伪造,缓解方式是严格验证 Digest 和 Signature 头;规模扩展时,迁移至 PostgreSQL,支持并发查询。

在 Mastodon 兼容网络中,这一服务器可无缝集成:用户从 Mastodon 关注本地 Actor,后续帖子自动出现在其时间线。实际部署时,使用 PM2 进程管理器运行 Node.js,确保高可用。测试工具如 fedify CLI 可模拟联邦交互。总之,这一实现不仅体现了 ActivityPub 的强大,还为去中心化社交提供了可落地路径,推动开放 web 的发展。(1024 字)

查看归档