news 2026/9/15 21:27:11

Owncast 联邦(Federation)互操作指南:ActivityPub、WebFinger、NodeInfo 与 Featured Streams 协议全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Owncast 联邦(Federation)互操作指南:ActivityPub、WebFinger、NodeInfo 与 Featured Streams 协议全解析

Owncast 联邦(Federation)互操作指南:ActivityPub、WebFinger、NodeInfo 与 Featured Streams 协议全解析

【免费下载链接】owncastTake control over your live stream video by running it yourself. Streaming + chat out of the box.项目地址: https://gitcode.com/GitHub_Trending/ow/owncast

Owncast 是一款自托管的直播服务器,其联邦子系统让每个实例以单个 ActivityPub Actor 的身份接入 Fediverse:对外广播开播通知与图文帖,同时通过轻量级 Featured Streams(特色直播)目录协议相互罗列彼此的直播状态。本文基于仓库根目录的 FEDERATION.md 为主干,结合services/activitypub源码与持久化实现,系统讲解 Owncast 支持的标准协议、Actor 模型、活动类型矩阵、流状态信令(Offer/Leave)、引文帖(FEP-044f)、HTTP 签名与隐私模式,帮助 Fediverse 软件运营者和开发者理解并实现与 Owncast 的互操作。

Owncast 联邦的设计哲学

Owncast 的联邦遵循 FEP-67ff(FEDERATION.md 规范),其核心定位是:

  • 每个实例以单个Service类型 Actor(而非Person)代表自身及其直播;
  • 广播开播(go-live)公告和图文帖给粉丝(followers);
  • 不呈现社交时间线,也不关注单个联邦账户;
  • 服务器之间可以通过 Featured Streams 协议相互罗列彼此的直播状态。

这种设计使 Owncast 在 Fediverse 中更像一个"广播节点"而非"社交节点"。从代码结构看,联邦子系统在 services/activitypub/activitypub.go 中作为组合根(composition root)装配,串联了persistenceworkerpool(投递队列)、outbox(出站)、inbox(入站)、jobscontrollers六个子服务,并向流/管理端暴露SendLiveSendPublicFederatedMessageGetFollowerCount等小型 API。

支持的协议与 FEP 标准

Owncast 联邦层支持四类基础标准:

标准用途
ActivityPub(Server-to-Server)活动的发送与接收
WebFingerActor 发现(acct:形式的资源标识)
HTTP Signatures出站请求签名与入站验签
NodeInfo实例元信息描述

同时实现了三个 Fediverse Enhancement Proposals:

  • FEP-67ff:本仓库根目录的 FEDERATION.md 本身即是该 FEP 描述的规范形态;
  • FEP-f1d5:NodeInfo 在 Fediverse 软件中的应用;
  • FEP-044f:尊重用户同意的引文帖(quote posts)。

Actor 模型:以 Service 类型暴露直播实例

每个 Owncast 实例对外呈现为一个ServiceActor。其 profile 构成(见 apmodels/actor.go 中MakeServiceForAccount的实现)包括:

  • icon:服务器 Logo / 头像;
  • image:profile 横幅图(当前与icon为同一资源);
  • summary:服务器描述,按配置原样输出(来自GetServerSummary());
  • attachment:由PropertyValue对象组成的数组,承载社交链接与元数据(源码中通过addMetadataLinkToProfile追加Stream链接与各社交平台句柄,若社交链接恰好只有一条,还会额外补一条Owncast链接以规避单元素 attachment 序列化不成数组的问题);
  • tag:描述服务器内容的标签(Hashtag,来自GetServerMetadataTags());
  • manuallyApprovesFollowers:私密模式下为true(取自GetFederationIsPrivate());
  • discoverable:恒为true

Owncast不暴露following集合,该端点返回404;它不关注单个联邦账户,但会以目录方式"关注"其他服务器以构建 Featured Streams(详见下文)。

活动类型矩阵:Send / Receive 全景

FEDERATION.md 给出了完整的活动矩阵,逐项解读如下:

ActivityObjectSendReceive说明
CreateNote开播时与手动联邦发帖时发送;入站 Note 仅在有提及/回复时产生插件事件
UpdateService服务器 profile 变更时发送
UpdatePerson更新缓存的粉丝 profile 信息
Follow-粉丝关注(私密模式排队待审);目录关注(Featured Streams)也走 Follow
AcceptFollow接受入站关注;或收到对方接受我们的目录关注
RejectFollow拒绝/移除粉丝;或收到对方拒绝目录关注
UndoFollow入站取消关注;出站取消罗列某服务器
LikeNote可选展示在直播聊天中
AnnounceNote可选展示在直播聊天中
Offer-Featured Streams 直播中信号
Leave-Featured Streams 离线信号
QuoteRequestNote远程用户请求引用我们的帖子(FEP-044f)
AcceptQuoteRequest帖子存在且允许引用时发送,result携带QuoteAuthorization印章 IRI
RejectQuoteRequest未知帖子、私密模式或引用被禁用时发送

关键限制:Owncast不会把入站Create(Note)作为社交时间线展示,也不会向外转发;符合条件的提及与回复只投递给服务器插件。入站活动的处理入口在 inbox 包内,每个活动类型对应一个 handler(create.gofollow.golike.goannounce.goquote.gooffer.goleave.goupdate.goundo.goaccept.goreject.go等),通过 inbox/service.go 分发给 worker。

Featured Streams:Owncast 直播状态目录协议

这是 Owncast 联邦中最具特色的部分,允许服务器以轻量目录方式罗列彼此的直播。协议基于标准 ActivityPub 活动,外加https://owncast.online/ns#命名空间下的一小组 JSON-LD 扩展属性。

关注(罗列)一台服务器

目录向目标服务器发送Follow,要求:

  • actor为目录自身;
  • object为目标服务器的 Actor;
  • 携带扩展属性https://owncast.online/ns#directory: true,用于区分"目录罗列"与"普通个人关注"。

目录关注始终需要运营者审批(即便在公开实例上)。目标服务器审批后回复Accept(Follow),拒绝则回复Reject(Follow);后续目标服务器可再发Reject(Follow)撤销罗列,目录方也可发送Undo(Follow)停止罗列。

从源码看,Owncast 作为目录方向外发出 Follow 时,通过 owncast_metadata.go 中的SetBasicOwncastMetadata写入ns#directory标记;而作为被罗列方收到携带该标记的 Follow 时,会在 apmodels/actor.go 的ActivityPubActor.IsDirectory字段中记录,并进入运营者审批队列。

直播中 / 离线信号

一旦关注被接受,Owncast 服务器只向其目录粉丝(而非普通个人粉丝)投递流状态活动:

  • Offer:直播开始时发送,之后由定时器周期性重发(StartStreamPingTicker/SendStreamPing,见 activitypub.go),保证目录状态新鲜;
  • Leave:直播结束时发送(SendStreamGoingOffline),使目录立即将该服务器从"直播中"列表移除,无需等待过期清扫(staleness sweep)。

两者的object均使用发送服务器的基 URL(scheme://host),流元数据以 JSON-LD 扩展属性附加。这些扩展属性的键定义在 config/constants.go:

属性(https://owncast.online/ns#含义
streamStatusliveoffline
streamTitle当前直播标题
streamDescription直播或服务器描述
serverName服务器显示名
logoUrl服务器 Logo URL
thumbnailUrl直播预览缩略图 URL(仅直播中存在)
streamTags流标签数组

出站侧,SetOwncastMetadata在开播时自动附加thumbnailUrl{serverURL}/thumbnail.jpg)与streamStatus: live;入站侧,inbox/offer.go 的handleOfferInboxRequest会先校验该活动确为streamStatus == "live"的合法 Offer、再确认该服务器处于"正在被我们关注"的状态(未关注或pending/rejected的服务器直接忽略),随后调用UpdateServerStatus写入数据库。

元数据截断防护:远程元数据被视作不可信输入,存储前会做长度与数量钳制(见 inbox/offer.go):

  • 流标题上限 300 字符;
  • 流描述上限 2000 字符;
  • 服务器名上限 200 字符;
  • URL 上限 2048 字符;
  • 标签最多 20 个、每个最长 100 字符,且按 rune(UTF-8 安全)截断。

对应测试见 metadata_clamp_test.go。持久化侧,联邦服务器记录按scheme://host基 URL 作为主键,存储于federated_servers表(仓库 persistence/federatedserversrepository 包,含UpdateServerStatus等操作),并记录"最后在线时间"与"最后状态更新时间"(可对照 server-federation-2.md 的设计要求)。

与其他软件的互操作

该协议是开放的,但两个方向目前能力不同:

  • 入站(第三方作为目录罗列 Owncast):Owncast 接受任何携带ns#directory标记的目录 Follow,并向其投递Offer/Leave,因此任何第三方 ActivityPub 实现都可以充当罗列 Owncast 直播的目录;
  • 出站(Owncast 作为目录罗列他人):目前仅限 Owncast 自身——在关注某台服务器以便罗列其直播前,Owncast 会先校验对方 NodeInfo 报告的software.name == "owncast"metadata.federation.featuredStreams >= 1(见 utils/nodeinfo.go 中的校验逻辑),因此不会罗列非 Owncast 服务器。

Note 对象结构与开播公告

Owncast 发出的帖子为Note对象,包含:

  • content:带链接标签的 HTML 格式文本;
  • attachment:直播预览的Image对象(开播帖存在预览图时附带;源码在SendLive中优先选用preview.gif,其次thumbnail.jpg);
  • tagHashtagMention对象的数组;
  • sensitive:直播标记为 NSFW 时,开播帖为true(对应GetNSFW()配置);
  • interactionPolicy:公开帖默认声明任何人都可引用(canQuoteautomaticApproval指向公共集合),除非引用被禁用或服务器处于私密模式。

开播公告:直播开始时发送Create(Note),内容包含配置的开播消息(默认I've gone live!)、服务器配置的标签、观看链接以及可用的直播预览图。若运营者清空了开播消息,则不发送公告(SendLivetextContent == ""时直接返回,见 outbox.go)。标签使用Hashtag类型(Mastodon/toot词表),链接指向https://owncast.directory/tags/{tag}以便跨实例发现;此外代码还会自动补充#owncast标签,确保出现在 Owncast 搜索结果中。

引文帖(Quote Posts)与 FEP-044f

Owncast 实现了 FEP-044f 的被引用侧(quoted-server side),为远程用户引用 Owncast 帖子提供同意流程:

  • 公开帖携带interactionPolicy,声明自动批准任何人引用;
  • 收到针对本服务器所创作帖子的入站QuoteRequest时,以Accept应答,其result为已存储的QuoteAuthorization印章的 IRI;该印章可在该 IRI 上被解引用,供第三方服务器验证引用已获批准(见 inbox/quote.go,印章通过apmodels.MakeQuoteAuthorization构建并存储,然后经requests.SendQuoteRequestAccept投递);
  • 对未知对象的QuoteRequest、私密模式下或引用被禁用时的任何请求,均回复Reject
  • 引用默认启用,运营者可关闭(配置项GetFederationEnableQuotes());
  • Owncast不主动创作引文帖,也不撤销已签发的印章。

寻址(Addressing)

公开帖的寻址结构为:

{ "to": ["https://www.w3.org/ns/activitystreams#Public"], "cc": ["{actor}/followers"] }

Owncast 还可向特定 Actor 发送私信(用于回复互动),此时收件人放入to,并附带对应的Mention标签以兼容 Mastodon(实现见SendDirectMessageToAccount:先经 WebFinger 解析账户、解析 Actor,再将活动与 Note 改为直邮寻址)。

HTTP Signatures:出站签名与入站验签

  • 出站:所有POST请求使用RSA-SHA256签名,被签名头为(request-target)hostdatedigest;每次投递尝试都会生成全新签名。对应实现位于 crypto/sign.go:httpsig.RSA_SHA256算法、DigestSha256摘要,headersToSign按需追加digest。出站侧在 outbox.go 的sendToInboxes中为每个收件箱构建workerpool.Delivery,签名在每次持久化投递尝试前即时生成。
  • 入站:活动以HTTP 202 Accepted接收(见 controllers/inbox.go),随后异步验签。仅携带有效 HTTP 签名的活动会被处理;未签名或签名无效的活动被丢弃、不执行任何动作。
  • 对 actor、outbox、followers 端点的GET 请求不需要签名

私密模式(Private Mode)

私密模式下:

  • Actor 的manuallyApprovesFollowerstrue
  • 关注请求进入运营者审批队列(GetPendingFollowRequests可查询待审列表);
  • 仅在人工批准后发送AcceptRespondToFollow中原子地记录审批结果并将 Accept/Reject 入队投递,见 activitypub.go)。

屏蔽(Blocking)

Owncast 支持域名级单个 Actor 级两种屏蔽。来自被屏蔽域名或被禁用 Actor 的入站活动会被直接丢弃而不处理;被拒绝或屏蔽的粉丝会从未来的出站投递中移除。管理端移除粉丝时(RemoveFollower),若该粉丝是目录,还会尽力投递一个Reject(Follow)通知其撤销罗列(见 activitypub.go)。

WebFinger:Actor 与直播流发现

Actor 发现通过/.well-known/webfinger?resource=acct:{username}@{domain}进行。处理器在 controllers/webfinger.go 中:联邦未启用时返回405,主机无法规范化返回404resource参数畸形返回400,请求的主机与实例主机不一致返回501

响应中的 links(构建逻辑见 apmodels/webfinger.go)包括:

RelType指向
selfapplication/activity+jsonActor 文档
http://webfinger.net/rel/profile-pagetext/htmlWeb 资料页
http://webfinger.net/rel/avatar图片类型Profile 头像
alternateapplication/x-mpegURLHLS 流 URL

其中alternate链接是 Owncast 的特色:客户端可直接从 WebFinger 发现直播流(HLS)地址,而不必先加载 Actor 文档。

NodeInfo:实例元信息

NodeInfo 发现入口为/.well-known/nodeinfo,NodeInfo 2.0 版本在/nodeinfo/2.0提供(见 controllers/nodeinfo.go:v2.Path = "nodeinfo/2.0",link rel 为http://nodeinfo.diaspora.software/ns/schema/2.0)。相关端点还有:

  • /.well-known/x-nodeinfo2:扩展格式;
  • /api/v1/instance:Mastodon 兼容的实例信息;
  • /.well-known/host-meta:WebFinger 发现。

在 Featured Streams 出站校验中,NodeInfo 是判断对方是否为 Owncast 实例(software.name)及其是否支持特色直播(metadata.federation.featuredStreams >= 1)的依据,同时metadata.federation.username还用于提取联邦用户名(utils/nodeinfo.go 的ExtractFederationUsername)。

互操作注意事项速查

  • Owncast 使用ServiceActor 类型,而非Person
  • Owncast 不关注个人联邦账户,也不暴露following集合,但服务器之间通过 Follow 构建 Featured Streams 目录;
  • 入站Create(Note)不作为时间线帖子展示、也不向外转发,符合条件的提及/回复只投递给插件;
  • LikeAnnounce互动可在启用后展示在直播聊天中(由 chat 的入站处理与聊天事件系统协作完成);
  • sensitive标记表示整个流为 NSFW 内容。

相关文档与实现索引

  • 协议总纲:FEDERATION.md(FEP-67ff 的实现描述)
  • 联邦子系统组合根与公开 API:services/activitypub/activitypub.go
  • 入站活动处理(Offer/Leave/Quote/Follow 等):services/activitypub/inbox
  • 出站消息生产:services/activitypub/outbox/outbox.go
  • 扩展元数据解析与写入:services/activitypub/apmodels/owncast_metadata.go
  • 命名空间常量定义:config/constants.go
  • 联邦服务器持久化:persistence/federatedserversrepository
  • 设计历史与演进:docs/features/server-federation-2.md、docs/features/server-federation-4.md、docs/features/server-federation-security.md
  • 入站活动持久化模型:models/federatedActivity.go、models/federatedserver.go

【免费下载链接】owncastTake control over your live stream video by running it yourself. Streaming + chat out of the box.项目地址: https://gitcode.com/GitHub_Trending/ow/owncast

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 21:23:41

UART通信原理与实战:从时序契约到工业级调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 21:23:08

Transformer原理与PyTorch实现:从注意力机制到代码实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 21:22:35

混凝土ERP选型:聚焦时间熔断与动态配比的刚性约束

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 21:22:06

驾照考试系统源码解析:Java与PHP双后端协作实战

简介:这是一套基于Java、JavaScript、CSS、HTML、PHP等多种语言开发的驾照考试系统完整源码,面向需要学习全栈Web开发或直接部署驾考平台的开发者、学生及项目实践者,也可作为二次开发的基础。资源共300个文件,其中含78个Java源文…

作者头像 李华