Subscribe to receive notifications of new posts:

欢迎光临 Wildebeest:Cloudflare 上的 Fediverse

02/08/2023

26 min read
Welcome to Wildebeest: the Fediverse on Cloudflare

最近,Fediverse 一直是一个热门话题,成千上万甚至上十万新用户在 Mastodon 等平台上创建帐户,或完全转移到“另一端”,或试用和了解这一全新的社交网络。

今天我们要介绍的是 Wildebeest。这是一个易于部署的开源服务器,与 ActivityPub 和 Mastodon 兼容,完全构建在 Cloudflare 的 Supercloud 之上。若想在 Fediverse 中运行自己的服务器,现在您可以完全通过 Cloudflare 实现。

构建在 Cloudflare 上的 Fediverse。

若想加入 Mastodon 联盟网络,现在您有两个选择:或加入现有的服务器(服务器也称社区,每个社区都有自己的基础设施和规则),或运行自己的自托管服务器。

想运行自己的服务器有以下几种原因:

  • 您想创建一个新的社区,并通过共同的主题和使用规来吸引其他用户。
  • 您不希望必须信任第三方服务器并遵守他们的策略,您希望您的服务器在您自己的域中为您的个人帐户服务。
  • 您希望完全控制自己的数据、个人信息和内容,并了解实例的运行情况。

非营利组织 Mastodon gGmbH 使用 Ruby、Node.js、PostgreSQL 和 Redis 来帮助实现服务器。不过,运行官方服务器可能存在挑战。您需要在某个地方拥有或租赁一台服务器或虚拟专用服务器 (VPS);您必须安装和配置软件,设置数据库和面向公众的 web 服务器,配置和保护您的网络免遭攻击或滥用。然后,您必须维护所有这些设备,并不断更新。您需要处理大量的脚本和技术工作才能启动和运行;非技术爱好者绝对不想这么麻烦。

Wildebeest 的宗旨:让您可以在 Cloudflare 上快速部署与 Mastodon 兼容的自有服务器,并在几分钟内连接到 Fediverse;无需担心维护、滥用或攻击问题;Cloudflare 会自动为您处理这些工作。

Wildebeest 不是一项托管服务。它是您的实例、数据和代码,以您的 Cloudflare 帐户在我们的云中运行。而且它是一项开源服务,这意味着它会不断发展,将拥有更多功能,任何人都可以扩展并改进。

目前我们支持:

  • ActivityPubWebFingerNodeInfoWebPushMastodon 兼容的 API。Wildebeest 可以连接其他 Fediverse 服务器或接收来自其他 Fediverse 服务器的连接。
  • 与最常用的 Mastodon web(例如 Pinafore)、桌面和移动客户端兼容。我们还提供一个简单的只读 web 界面来浏览时间线和用户档案。
  • 可以发布、编辑、推广或删除帖子、sorry 帖、嘟文。我们支持文本、图像,马上还将支持视频。
  • 任何人都可以关注您;您也可以关注任何人。
  • 可以搜索内容。
  • 可以在您的实例下注册一个或多个帐户。可使用电子邮件,或与 Cloudflare Access 兼容的任何 IdP(例如 GitHub 或 Google)进行身份验证。
  • 可以编辑您的档案信息、头像和标头图像。

我们的网络是如何打造的

这些实现完全以我们的产品API 为基础。构建 Wildebeest 是另一个绝佳机会,可以展示我们功能强大、用途广泛的技术栈,证明任何人都可以使用 Cloudflare 来构建涉及多个系统和复杂要求的大型应用程序。

Wildebeest 架构的鸟瞰图如下:

现在我们来了解其细节和技术。

Cloudflare 页面

本质上而言,Wildebeest 是一个使用 Pages Functions 运行代码的 Cloudflare Pages 项目。Cloudflare Pages 为构建和部署应用程序、支持您的捆绑资产提供了很好的基础,Functions 则让您能够完全访问 Workers 生态系统,您可在其中运行任何代码。

Functions 有一个基于文件的内置路由器/functions 目录结构,按 Wildebeest 的持续部署版本上传,它定义了您的应用程序路由以及哪些文件和代码负责处理每个 HTTP 端点请求。这种路由技术与 Next.js 等框架使用的技术相似。

例如,Mastodon 的 /api/v1/timelines/public API 端点由 /functions/api/v1/timelines/public.ts 使用 onRequest 方法处理。

export onRequest = async ({ request, env }) => {
	const { searchParams } = new URL(request.url)
	const domain = new URL(request.url).hostname
...
	return handleRequest(domain, env.DATABASE, {})
}

export async function handleRequest(
    …
): Promise<Response> {
    …
}

端点单元测试也变得更加简单,只需从测试框架中调用 handleRequest() 函数即可。请参阅我们的一个 Jest 测试——mastodon.spec.ts

import * as v1_instance from 'wildebeest/functions/api/v1/instance'

describe('Mastodon APIs', () => {
	describe('instance', () => {
		test('return the instance infos v1', async () => {
			const res = await v1_instance.handleRequest(domain, env)
			assert.equal(res.status, 200)
			assertCORS(res)

			const data = await res.json<Data>()
			assert.equal(data.rules.length, 0)
			assert(data.version.includes('Wildebeest'))
		})
       })
})

与其他普通 Worker 一样,Functions 也允许设置绑定,以便与其他 Cloudflare 产品和功能交互,例如 KVR2D1Durable Objects,等等。可交互的产品和功能还在不断增加。

我们使用 Functions 来实现官方 Mastodon API 规范的大部分内容,使 Wildebeest 与其他服务器和客户端应用程序的现有生态系统兼容,同时在同一项目代码库中运行我们自己的只读 web 前端。

Wildebeest 的 web 前端使用 Qwik。Qwik 是一个旨在加快速度的多用途 web 框架,使用 JSX JavaScript 语法扩展等现代概念,支持服务器端渲染 (SSR) 和静态网站生成 (SSG)。

Qwik 提供一个开箱即用的 Cloudflare Pages Adaptor,所以我们可以直接使用(查看我们的框架指南,进一步了解如何在 Cloudflare 页面上部署 Qwik 网站)。样式设计方面,我们使用 Qwik 原生支持的 Tailwind CSS 框架。

可通过 /frontend 目录查找我们的前端网站代码和静态资产。应用程序由 /functions/[[path]].js 动态路由处理,它基本上能捕捉所有非 API 请求,然后调用 Qwik 自己的内部路由器 Qwik City,由其负责后面的所有工作。

Pages 和 Functions 路由功能强大,用途广泛,使得在同一项目下同时运行后端 API 和服务器端渲染的动态客户端(实际上是一个全栈应用程序)成为可能。

现在我们来深入了解服务器如何与我们架构中的其他组件交互。

D1

Wildebeest 使用 D1。D1 是 Cloudflare 为 Workers 平台构建的第一个 SQL 数据库,以 SQLite 为基础,现在向所有人开放 α 测试,用于存储和查询数据。我们的模式如下:

随着功能不断增多,未来模式可能会发生改变。但没关系,D1 支持迁移,当您需要更新数据库模式时,不会丢失数据。使用每个新版 Wildebeest 时,如果需要改变数据库模式,我们可以创建一个新的迁移文件。

-- Migration number: 0001 	 2023-01-16T13:09:04.033Z

CREATE UNIQUE INDEX unique_actor_following ON actor_following (actor_id, target_actor_id);

D1 支持构建强大的客户端 API,帮助开发者操作和查询 Worker 脚本的数据。在我们的例子中就是 Pages Functions。

下面举一个简化的例子,说明当您在 Fediverse 上开始关注某人时,我们如何与 D1 交互。

export async function addFollowing(db, actor, target, targetAcct): Promise<UUID> {
	const query = `INSERT OR IGNORE INTO actor_following (id, actor_id, target_actor_id, state, target_actor_acct) VALUES (?, ?, ?, ?, ?)`
	const out = await db
		.prepare(query)
		.bind(id, actor.id.toString(), target.id.toString(), STATE_PENDING, targetAcct)
		.run()
	return id
}

Cloudflare 的文化倡导内部测试,在自己的产品基础上构建,这意味着我们有时会比用户先发现产品的不足。使用 D1 确实给我们带来了一些挑战,D1 以 SQLite 为基础,用于存储我们的数据。下面来看两个例子。

ActivityPub 使用 UUID 来识别对象,并在 URI 中广泛引用。这些对象需要存储在数据库中。PostgreSQL 等其他数据库则提供内置函数来产生唯一标识符。SQLite 和 D1 目前没有这个功能,但已列入我们的计划。

别担心,Workers 运行时支持 Web Crypto,所以我们使用 crypto.randomUUID() 来获取唯一标识符。请参阅 /backend/src/activitypub/actors/inbox.ts

export async function addObjectInInbox(db, actor, obj) {
	const id = crypto.randomUUID()
	const out = await db
		.prepare('INSERT INTO inbox_objects(id, actor_id, object_id) VALUES(?, ?, ?)')
		.bind(id, actor.id.toString(), obj.id.toString())
		.run()
}

问题就解决了。

另一个例子是,我们需要存储具有亚秒级分辨率的日期。同样,PostgreSQL 等数据库也有该功能。

psql> select now();
2023-02-01 11:45:17.425563+00

但 SQLite 的不足之处在于:

sqlite> select datetime();
2023-02-01 11:44:02

我们通过一个小型 hack 程序,使用 strftime() 解决了这个问题:

sqlite> select strftime('%Y-%m-%d %H:%M:%f', 'NOW');
2023-02-01 11:49:35.624

请参阅我们的初始 SQL 模式,查找 cdate 默认值。

Images

Mastodon 的内容有大量富媒体。我们不需要重造轮子,重建图像管道;Cloudflare Images 提供 API,从我们的全球 CDN 上传、转换和提供优化的图像,完全符合 Wildebeest 的要求。

发布内容图像、档案头像或标头等,都是使用图像 API。请参阅 /backend/src/media/image.ts,了解我们如何与 Images 配合使用。

async function upload(file: File, config: Config): Promise<UploadResult> {
	const formData = new FormData()
	const url = `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/images/v1`

	formData.set('file', file)

	const res = await fetch(url, {
		method: 'POST',
		body: formData,
		headers: {
			authorization: 'Bearer ' + config.apiToken,
		},
	})

      const data = await res.json()
	return data.result
}

如有兴趣在下一个项目中考虑使用 Images,请参阅此教程:如何在您的网站上集成 Cloudflare Images

也可从仪表板访问 Cloudflare Images。可以用它来快速浏览或管理您的目录。

队列

ActivityPub 协议是为聊天设计的。根据社交图谱的大小,可能会有很多来回传递的 HTTP 流量。我们不能在每次有人发帖时,都让客户端等待数百条 Fediverse 消息的传送,造成拥堵。

我们需要寻找一种方法,既支持异步工作,又启动后台作业,将数据处理从主应用程序中分离出来,以保持客户端的敏捷性。官方的 Mastodon 服务器有一个类似策略,使用 Sidekiq 负责后台处理。

幸运的是,我们也不需要担心这些复杂的问题。Cloudflare Queues 让开发人员能够安心无虞地发送和接收消息,为 Workers 请求分担一些工作,从而有效提供异步批量作业的能力。

简而言之,您有一个队列主题标识符——本质上是一个自动扩展的缓冲列表;然后您有一个或多个生产者生产结构化的消息(在我们的例子中就是 JSON 对象),并把它们放入队列(模式由您定义);最后您有一个或多个消费者订阅该队列,接收消息并按自己的速度处理消息。

请参阅 Queues 工作原理页面,了解更多信息。

在我们的案例中,每当任何传入的 API 调用需要时间长、成本高的操作时,主应用程序就会产生队列作业。例如,有人发布帖子、sorry 帖或嘟文时,我们需要将其传送到粉丝的收件箱,可能会引发许多对远程服务器的请求。这时,我们为此排列作业,将 API 释放出来确保继续响应:

export async function deliverFollowers(
	db: D1Database,
	from: Actor,
	activity: Activity,
	queue: Queue
) {
	const followers = await getFollowers(db, from)

	const messages = followers.map((id) => {
		const body = {
			activity: JSON.parse(JSON.stringify(activity)),
			actorId: from.id.toString(),
			toActorId: id,
		}
		return { body }
	})

	await queue.sendBatch(messages)
}

同样,远程服务器向我们的实例收件箱传送消息时,我们也不希望停止主 API。Wildebeest 在收件箱接收消息时,创建异步作业的方法如下:

export async function handleRequest(
	domain: string,
	db: D1Database,
	id: string,
	activity: Activity,
	queue: Queue,
): Promise<Response> {
	const handle = parseHandle(id)

	const actorId = actorURL(domain, handle.localPart)
const actor = await actors.getPersonById(db, actorId)

      // creates job
	await queue.send({
		type: MessageType.Inbox,
		actorId: actor.id.toString(),
		activity,
	})

	// frees the API
	return new Response('', { status: 200 })
}

最后一环是我们的队列消费者,它们在一个独立的 Worker 中运行,独立于 Pages 项目。消费者监听新的消息,并按自己的节奏依次处理,将其他人从拥堵中解救出来。繁忙时,队列会增加缓冲区。尽管如此,一切仍在运行,作业最终会全部分派,将主要的 API 释放出来处理关键的事情:尽可能快速响应远程服务器和客户端。

export default {
	async queue(batch, env, ctx) {
		for (const message of batch.messages) {
			…

			switch (message.body.type) {
				case MessageType.Inbox: {
					await handleInboxMessage(...)
					break
				}
				case MessageType.Deliver: {
					await handleDeliverMessage(...)
					break
				}
			}
		}
	},
}

如果您想亲自体验 Queues,可以试试这个简单的例子:使用 Queues 在 R2 中存储数据

缓存和 Durable Objects

缓存重复性操作也可以提高需要数据处理的复杂应用程序的性能。著名的 Netscape 开发者 Phil Karlton 曾说:“计算机科学只有两件难事:缓存失效和命名。”

Cloudflare 显然非常了解缓存,毕竟缓存是我们全球 CDN 的一个核心功能。我们还向客户提供 Workers KV——一个低延迟的全球键值数据存储,任何人都可以用它在我们的数据中心缓存数据对象,构建快速的网站和应用程序。

但 KV 实现了最终一致的性能。虽然这对许多应用程序和用例来说还不错,但对其他应用程序和用例来说却不太理想。

ActivityPub 协议高度事务性,无法实现最终一致性。例如:生成完整的时间线成本高昂,所以我们会缓存该操作。然而,发布帖子时,我们需要在缓存失效后再回复客户端。否则,新的帖子就不会出现在时间线上,客户端可能会因为没有看到它而出错,导致发帖失败。这其实就是我们最流行的客户端上发生过的情况。

我们需要更加聪明。团队讨论了几个选项。幸运的是,我们的 API 目录有很多选择。例如 Durable Objects

Durable Objects 是提供事务性存储 API 的单实例 Workers。需要中央协调性、强一致性和状态持久性时,Durable Objects 便是理想之选。处理多个 WebSocket 连接的状态、协调和路由聊天室消息,甚至运行多人游戏(例如 Doom)等用例中均可使用 Durable Objects。

现在您已经有所了解。是的,我们在 Durable Objects 的基础上,为 Wildebeest 实现了键值缓存子系统。通过利用 DO 的原生事务性存储 API,我们可以有力地保证,当我们创建或更改一个键后,下次读取时将总是会返回最新的版本。

这个想法非常简单有效,我们只用了几行代码,就使用两种原语(HTTP PUT 和 GET)实现了键值缓存。

export class WildebeestCache {
	async fetch(request: Request) {
		if (request.method === 'GET') {
			const { pathname } = new URL(request.url)
			const key = pathname.slice(1)
			const value = await this.storage.get(key)
			return new Response(JSON.stringify(value))
		}

		if (request.method === 'PUT') {
			const { key, value } = await request.json()
			await this.storage.put(key, value)
			return new Response('', { status: 201 })
		}
	}
}

强一致性就是这样。现在我们来看看用户注册和身份验证。

Zero Trust Access

官方的 Mastodon 服务器处理用户注册(通常使用电子邮件注册)之后,您才能选择您的本地用户名,开始使用该服务。如果我们从头开始构建,处理用户注册和身份验证可能非常耗时,令人生畏。

此外,人们不希望为想要使用的每一项新服务创建新的凭证,而是希望有更方便的授权和身份验证方法,就像 OAuth 那样,可以使用他们现有的 Apple、Google 或 GitHub 帐户。

我们希望利用 Cloudflare 的内置功能进行简化。显然,我们有一款产品可以为 Cloudflare 保护的任何应用程序处理用户引导、身份验证和访问策略;它就是 Zero Trust。因此,我们把 Wildebeest 部署在它后面。

Zero Trust Access 可以使用电子邮件进行一次性 PIN (OTP) 身份验证,或使用多种标识提供程序(例如 Google、Facebook、GitHub、LinkedIn)进行单点登录 (SSO),包括任何支持 SAML 2.0 的常规标识提供程序

开始在客户端上使用 Wildebeest 时,根本不需要注册。您可以直接登录,然后将重定向到 Access 页面,并根据您自己(您的实例的所有者)配置的策略来处理身份验证。

该策略定义了谁可以通过身份验证,以及如何验证。

通过身份验证后,Access 将把您重定向到 Wildebeest。第一次这样登录时,如果我们没有该用户的信息,我们会检测出来,并要求提供用户名和显示名称。这些问题只会问一次,并直接用于创建您的公开 Mastodon 档案。

在技术上,Wildebeest 实现了 OAuth 2 规范。Zero Trust 保护 /oauth/authorize 端点,并在用户通过身份验证后,在请求标头中出具一个有效的 JWT 令牌。然后 Wildebeest 读取并验证 JWT,并在 URL 重定向中返回一个授权码。

客户端获得授权码后,就可以使用 /oauth/token 端点来获取 API 访问令牌。随后的 API 调用会在授权标头中注入一个持有者令牌。

Authorization: Bearer access_token

部署和持续集成

我们不想为 Mastodon 运行托管服务,因为这会在一定程度上削弱联盟和数据所有权的概念。此外,我们必须承认 ActivityPub 和 Mastodon 是快速发展的新兴技术,将以难以预测的方式迅速演进。

因此,我们认为,现在帮助构建生态系统的最好方式是提供一个开源软件包,任何人都可以在我们的云上使用、定制、改进和部署。Cloudflare 显然会继续改进 Wildebeest,并支持社区,但我们希望 Fediverse 维护者可以完全控制和拥有其实例和数据。

但问题是,当 Wildebeest 需要配置这么多 Cloudflare 功能时,我们如何分发 Wildebeest 软件包并轻松部署到某人的帐户中,以及如何支持长期更新软件?

最终解决方案是将 GitHub 与 GitHub ActionsDeploy with WorkersTerraform 巧妙搭配。

用 Deploy with Workers 按钮是一个特制链接,可以自动生成工作流程页面,询问用户一些问题,Cloudflare 负责授权 GitHub 部署到 Workers,自动分叉 Wildebeest 存储库并克隆到用户的帐户中,然后使用 GitHub Actions 工作流程来配置和部署该项目。

GitHub Actions 工作流程 是一个 YAML 文件,其中说明了每一步操作。Wildebeest 工作流程(简化版)如下:

name: Deploy
on:
  push:
    branches:
      - main
  repository_dispatch:
jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - name: Ensure CF_DEPLOY_DOMAIN and CF_ZONE_ID are defined
        ...
      - name: Create D1 database
        uses: cloudflare/[email protected]
        with:
          command: d1 create wildebeest-${{ env.OWNER_LOWER }}
        ...
      - name: retrieve Zero Trust organization
        ...
      - name: retrieve Terraform state KV namespace
        ...
      - name: download VAPID keys
        ...
      - name: Publish DO
      - name: Configure
        run: terraform plan && terraform apply -auto-approve
      - name: Create Queue
        ...
      - name: Publish consumer
        ...
      - name: Publish
        uses: cloudflare/[email protected]
        with:
          command: pages publish --project-name=wildebeest-${{ env.OWNER_LOWER }} .

更新 Wildebeest

每当主分支发生变更,都会自动运行该工作流程,因此,更新 Wildebeest 就像将上游官方存储库与分叉同步一样简单。您甚至不需要使用 git 命令;GitHub 在用户界面中提供了同步按钮,点击该按钮即可,非常简单方便。

还有呢?更新是增量式的,不涉及销毁。将 GitHub Actions 工作流程重新部署 Wildebeest 时,我们只对您的配置做必要的修改,没有任何其他操作。您绝不会丢失数据;我们也不需要删除您现有的配置。我们的操作方法如下:

我们使用 Terraform,这是一种声明式配置语言和工具,可与我们的 API 交互,并可查询和配置您的 Cloudflare 功能。关键在于,每当我们应用一个新的配置时,我们会在一个 Cloudflare KV 键中为 Wildebeest 保留一份 Terraform 状态的副本。当触发新的部署时,我们可从 KV 副本中获取该状态,计算差值,只进行必要的修改。

也不会有数据丢失问题。如前文所述,D1 支持迁移。如果我们需要给表格增加一个新的列或需要新增一个表格,我们不需要销毁、重建数据库,我们只需要将必要的 SQL 应用于这一变更。

防护、优化和可观察性,轻而易举

一旦启动并运行 Wildebeest,您必须保护它免受不良流量和恶意行为者的影响。Cloudflare 在开箱后只需点击几下即可为您提供 DDoSWAF机器人管理保护。

同样,您将通过我们的产品获得即时的网络和内容交付优化,以及有关您的 Wildebeest 实例的性能和使用情况分析

ActivityPub、WebFinger、NodeInfo 和 Mastodon API

尽管是 Mastodon 普及了 Fediverse 的概念,但其使用的底层技术许多早已有之。这是一个珍贵的时刻,所有技术最终“齐聚一堂”,共同创建一个工作平台,回答互联网用户的实际用例问题。我们来快速浏览一下 Wildebeest 必须实现的协议:

ActivityPub

ActivityPub 是一个去中心化的社交网络协议,至少从 2018 年起就以 W3C 建议书的形式出现了。它定义了用于创建和操作内容的客户端 API,以及用于内容交换和通知的服务器到服务器 API,也称为联盟。ActivityPub 的词汇表使用 ActivityStreams,这是一个更老的 W3C 协议。

许多操作和活动的概念都是在 ActivityPub 规范中定义的。例如:Actors(档案)、消息或 Objects(嘟文)、收件箱(您通过收件箱接收关注对象的嘟文)和发件箱(您通过发件箱向关注对象发送嘟文)。

我们实现了 ActivityPub 的文件夹如下:

import type { APObject } from 'wildebeest/backend/src/activitypub/objects'
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'

export async function addObjectInInbox(db, actor, obj) {
	const id = crypto.randomUUID()
	const out = await db
		.prepare('INSERT INTO inbox_objects(id, actor_id, object_id) VALUES(?, ?, ?)')
		.bind(id, actor.id.toString(), obj.id.toString())
		.run()
}

WebFinger

WebFinger 是一个简单的 HTTP 协议,用于发现任何实体(例如配置文件、服务器或特定功能)的相关信息。它将 URI 解析为资源对象。

Mastodon 使用 WebFinger 查询来发现远程用户的信息。例如,假设您想与 @[email protected] 互动,您的本地服务器会请求 https://example.com/.well-known/webfinger?resource=acct:[email protected](使用 acct 方案)并得到类似这样的结果:

{
    "subject": "acct:[email protected]",
    "aliases": [
        "https://example.com/ap/users/user"
    ],
    "links": [
        {
            "rel": "self",
            "type": "application/activity+json",
            "href": "https://example.com/ap/users/user"
        }
    ]
}

现在我们已经知道如何使用 https://example.com/ap/users/user endpoint 端点与 @[email protected] 互动。

我们的 WebFinger 响应如下:

export async function handleRequest(request, db): Promise<Response> {
	…
	const jsonLink = /* … link to actor */

	const res: WebFingerResponse = {
		subject: `acct:...`,
		aliases: [jsonLink],
		links: [
			{
				rel: 'self',
				type: 'application/activity+json',
				href: jsonLink,
			},
		],
	}
	return new Response(JSON.stringify(res), { headers })
}

Mastodon API

最后,设置服务器信息、档案信息、生成时间线、通知和搜索等,都是 Mastodon 特定的 API。Mastodon 开源项目定义了一个 REST API 目录,您可以在他们的网站上查找所有文档。

可以在此处(REST 端点)和此处(后端原语)查看我们的 Mastodon API 实现。下面举一个由 Wildebeest 实现的 Mastodon 服务器信息 /api/v2/instance 示例。

export async function handleRequest(domain, db, env) {

	const res: InstanceConfigV2 = {
		domain,
		title: env.INSTANCE_TITLE,
		version: getVersion(),
		source_url: 'https://github.com/cloudflare/wildebeest',
		description: env.INSTANCE_DESCR,
		thumbnail: {
			url: DEFAULT_THUMBNAIL,
		},
		languages: ['en'],
		registrations: {
			enabled: false,
		},
		contact: {
			email: env.ADMIN_EMAIL,
		},
		rules: [],
	}

	return new Response(JSON.stringify(res), { headers })
}

Wildebeest 还为客户端通知实现了 WebPush,为服务器令牌实现了 NodeInfo

其他与 Mastodon 兼容的服务器必须实现所有这些协议;Wildebeest 便是其中之一。社区非常积极地讨论未来的增强功能;我们将不断提高我们的兼容性,并增加对更多功能的支持,确保 Wildebeest 在 Fediverse 服务器和客户端生态系统的形成过程中发挥积极作用。

立即开始使用

技术方面我们已经讨论了很多,下面我们来了解 Fediverse。我们尽可能详细说明部署服务器的所有步骤。要开始使用 Wildebeest,请前往公共 GitHub 存储库,并查阅我们的入门教程

Wildebeest 的大多数依赖项提供优惠的免费计划,允许您为个人或业余项目(与关键业务无关)试用它们,但您需要订阅一个 Images 计划(最低级的计划应该足以满足大多数需求),并视服务器负载情况订阅 Workers Unbound(同样,最便宜的计划应该足以满足大多数用例)。

我们倡导内部测试,因此,Cloudflare 今天也正式加入了 Fediverse。您可以开始关注我们的 Mastodon 帐户,并使用您最喜爱的 Mastodon 应用程序获取 Cloudflare 的定期更新,与通过其他社交平台获取更新的体验毫无二致。这些帐户完全在 Wildebeest 的服务器上运行。

Wildebeest 与大多数客户端应用程序兼容;我们已确认可与官方的 Mastodon AndroidiOS 应用程序、PinaforeMastodontooot 兼容,并还在研究与 Ivory 等其他应用程序的兼容性。如果您最喜爱的应用程序不能兼容,请在此提交问题,我们将尽力提供支持。

结语

Wildebeest 完全构建在我们的 Supercloud 栈上。这是我们创建的最完整、最复杂的项目之一,使用了 Cloudflare 的各种产品和功能。

希望这篇报道能激励您不仅尝试部署 Wildebeest,加入 Fediverse,并在 Cloudflare 上构建您的下一个应用程序,无论应用程序要求有多高。

Wildebeest 目前还只是一个非常简单可行、与 Mastodon 兼容的服务器,但我们会不断改进,推出更多功能,支持更多应用程序;毕竟,我们的官方帐户也在使用它。Wildebeest 是一种开源服务器,非常欢迎您通过拉动请求或反馈献计献策。

同时,我们在我们的 Developers Discord Server 上开设了Wildebeest 室,并始终关注 GitHub 存储库问题选项卡。欢迎与我们交流,我们渴望了解您是如何使用 Wildebeest 的,也乐意回答您的问题。

PS:为方便阅读并节省空间,本博客中的代码片段经过了简化(例如,删除了 TypeScript 类型和错误处理代码)。请参阅 GitHub 存储库的链接,查看完整版本。

We protect entire corporate networks, help customers build Internet-scale applications efficiently, accelerate any website or Internet application, ward off DDoS attacks, keep hackers at bay, and can help you on your journey to Zero Trust.

Visit 1.1.1.1 from any device to get started with our free app that makes your Internet faster and safer.

To learn more about our mission to help build a better Internet, start here. If you're looking for a new career direction, check out our open positions.
Cloudflare Pages (CN)D1 (CN)Cloudflare Workers (CN)Cloudflare Zero Trust (CN)简体中文

Follow on X

Celso Martinho|@celso
Sven Sauleau|@svensauleau
Cloudflare|@cloudflare

Related posts

November 17, 2022 2:00 PM

我们的构建过程:Cloudflare Radar 2.0 背后的技术

在上个月的 Cloudflare Birthday Week 期间,Radar 2.0 已作为完整的产品改进发布。这篇博客文章介绍了其技术上的构建过程。希望这篇文章能激励开发人员使用 Cloudflare 产品构建复杂的 Web 应用程序...