Help us shape the future of Go! Take the 2026 Go Developer Survey

federation

package
v0.32.41 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 31, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Overview

directory.go — 联邦节点名录透传(2026-10-14 邀请信箱配套;2026-10-22 由 直传包搬入)。

名录端点无鉴权(零敏感,公开信息),registry URL 由宿主从管理端 SystemConfig 注入(已过 SSRF 校验)。直传未启用/二进制缺失不挡名录查询—— 名录可见性只取决于 registry 可达。

归属:名录是对端站点列表,属联邦域,不属直传域——本文件不依赖 p2pc 运行时。

命名(2026-10-22 L3):PeerDirectory = 联邦节点名录,**无条件**构造 (可见性只取决于 registry 可达,与联邦运行时是否启用无关;见 invite.go 文件头的三类服务命名约定)。

Package federation 实现 PigeonBox 的 P2P 联邦面(跨实例口令路由 + 邀请交接)。

三个角色与构造条件(2026-10-22 命名统一)

本包导出三个服务,**构造条件不同**,这是设计使然、不是遗漏:

Node          本机节点运行时:节点身份(ed25519)、注册中心心跳、分享公告。
              仅在 federation.enabled 时构造;配置不合法时 bootstrap 降级
              为非联邦模式(单站功能不受影响)。
PeerDirectory 在线名录:列出联邦内可发现的对端。**无条件构造**——联邦关闭时
              名录仍要能读出「为什么不可用」,且注册中心地址来自配置而非运行态。
InviteMailbox  邀请信箱:待收邀请的出箱/留痕。**无条件构造**——收件箱在联邦
              关闭时也必须可用(邀请可以先到、联邦后开)。

与设备直传的关系

2026-10-22 Wave B' 起,Box 侧**不再编排设备直传**(原 p2px 域已整包删除), 直传本体归独立应用 PigeonBeam。本包只保留联邦面(口令路由 + 邀请信封交接)。

Package federation 将本站点接入 PigeonBox P2P 联邦(github.com/pigeonbox/p2p)。

职责(M2,设计文档 docs/specs/2026-10-04-p2p-registry-service-design.md §5):

  • 节点注册/心跳:Ed25519 签名租约,断线自动重注册 + 全量补公告(自愈);
  • 口令联邦路由:share 生命周期钩子公告/撤销 SHA-256(code)→本节点;
  • 解析代理:GET /api/v1/federation/resolve 转查 registry(浏览器免跨域)。

信任模型:本包只做"发现",取件方直连源节点、源节点本地校验口令——零跨节点信任。 签名负载格式与 p2p 仓 README 契约逐字节一致(见 client.go 常量注释)。 依赖方向:本包不 import 生态任何兄弟模块(叶子约束,core CI dep guard)。

本包三类服务按**构造条件**分家(2026-10-22 L3 命名收口,读者一眼可辨):

Service        联邦运行时(注册/心跳/公告/解析)——**仅联邦启用时**构造
               (NewService 在 !cfg.Enabled 时报错,由装配层降级为非联邦模式)。
InviteMailbox  联邦邀请收件箱——**无条件**构造:跨实例交接的凭证与状态机,
               与联邦运行时无关(联邦关闭时公开收件端点仍可用)。
PeerDirectory  联邦节点名录——**无条件**构造:可见性只取决于 registry 可达。

后两者的实例由装配层(bootstrap/wiring.go)构造,直传编排层只消费 (setter 注入)——删掉直传编排层时联邦能力不受影响(Wave B')。

invite.go — 联邦信封管理(跨实例推送邀请;2026-10-22 由直传包搬入)。

语义:邀请 = 口令 bearer(与手工交接同信任级——口令本身即授权,通道只是 递送);收件箱内存态(cap 20 / TTL 10 分钟 = p2pc 配对窗口,进程重启丢弃 可接受,口令同样已失效);出站推送目标 URL 走 SSRF 守卫(与 registry_url 同策略,私网目标需 security.ssrf.allow_private_networks)。

归属:信封是跨实例交接的凭证与状态机,属联邦域,不属直传域——本文件不 依赖 p2pc 运行时(无 spawn / 子进程 / 任务环),Wave B 删直传包时 本包原样留存。

命名(2026-10-22 L3):本包三类服务按"构造条件"分家,读者一眼可辨——

  • Service:联邦运行时(注册/心跳/公告),**仅联邦启用时**构造;
  • InviteMailbox:联邦邀请收件箱,**无条件**构造(联邦关闭时仍可用, 它是跨实例交接的凭证与状态机,与联邦运行时无关);
  • PeerDirectory:联邦节点名录,**无条件**构造(可见性只取决于 registry 可达,不取决于联邦是否启用)。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PushInvite added in v0.27.0

func PushInvite(ctx context.Context, targetURL, code, fromName, note string) error

PushInvite 把口令推送到目标实例的邀请收件端点(P3 免口令交接)。 targetURL = 目标实例基址(如 https://b.example.com); 实际 POST <target>/api/v1/p2p/invite,10s 超时。

Types

type DirectoryPeer added in v0.27.0

type DirectoryPeer struct {
	NodeID      string `json:"node_id"`
	Name        string `json:"name"`
	URL         string `json:"url"`          // 对外基址(NAT 后可为空)
	BoxPub      string `json:"box_pub"`      // X25519 密封公钥 hex
	OnlineSince int64  `json:"online_since"` // 租约起始 unix 秒
}

DirectoryPeer 名录对端(与 p2pd GET /v1/directory 条目同形)。

type Invite added in v0.27.0

type Invite struct {
	ID         string
	Code       string
	FromName   string
	Note       string
	ReceivedAt time.Time
}

Invite 待收邀请(收件箱条目)。

type InviteMailbox added in v0.27.0

type InviteMailbox struct {
	// contains filtered or unexported fields
}

InviteMailbox 联邦信封收件箱(内存态;不依赖 p2pc 运行时)。

无条件构造(联邦关闭时仍可用——收件箱是跨实例交接的凭证与状态机,不是 联邦运行时的附属物;见文件头命名约定)。

clock 为构造期注入的时钟(nil=time.Now);构造后不再变更,故读路径无需 加锁(与搬迁前直传包的 nowFn 语义一致)。

func NewInviteMailbox added in v0.27.0

func NewInviteMailbox(clock func() time.Time) *InviteMailbox

NewInviteMailbox 构造收件箱(clock 可注入;nil=真实时钟)。

func (*InviteMailbox) AddInvite added in v0.27.0

func (m *InviteMailbox) AddInvite(code, fromName, note string) (Invite, error)

AddInvite 邀请入箱(口令格式校验 + 同码去重 + 满箱拒绝)。

func (*InviteMailbox) ExpiresIn added in v0.27.0

func (m *InviteMailbox) ExpiresIn(inv Invite) int64

ExpiresIn 某条信封的剩余有效期(秒;已过期返回 0)。

供**已出箱**信封的展示方使用(认领端点返回信封时算剩余时间)—— 收件箱只对在箱条目给快照,出箱后条目已不在列表里,无处可查。 时钟同收件箱(构造期注入的时钟优先,测试用假时钟一致)。

func (*InviteMailbox) ListInvites added in v0.27.0

func (m *InviteMailbox) ListInvites() []InviteSnapshot

ListInvites 未过期邀请(新到在前)。

func (*InviteMailbox) TakeInvite added in v0.27.0

func (m *InviteMailbox) TakeInvite(id string) (Invite, error)

TakeInvite 认领(出箱);不存在/已过期报错。

type InviteSnapshot added in v0.27.0

type InviteSnapshot struct {
	ID         string    `json:"id"`
	Code       string    `json:"code"`
	FromName   string    `json:"from_name"`
	Note       string    `json:"note"`
	ReceivedAt time.Time `json:"received_at"`
	ExpiresIn  int64     `json:"expires_in"` // 秒
}

InviteSnapshot 对外快照(含过期倒计时,前端渲染用)。

type Node added in v0.27.1

type Node struct {
	// contains filtered or unexported fields
}

Node 本机联邦节点运行时:节点身份(ed25519)+ 注册中心心跳 + 分享公告。 实现 share.FederationNotifier 窄接口(ShareCreated/ShareDeleted), 由 bootstrap 注入 share service。

命名(2026-10-22):原名 Service 太含糊——同包另有两个无条件构造的 角色(PeerDirectory 在线名录 / InviteMailbox 邀请信箱),「哪个是 federation 的主体」看不出来。改 Node 后三个角色一目了然:

Node          本机节点运行时(**联邦启用时才构造**)
PeerDirectory 在线名录(无条件构造——联邦关闭时名录仍要能读配置)
InviteMailbox  邀请信箱(无条件构造——收件箱在联邦关闭时也必须可用)

func NewNode added in v0.27.1

func NewNode(cfg conf.FederationConfig, nodeName string) (*Node, error)

NewNode 构造并加载/生成节点身份密钥。仅在 federation.enabled 时调用; 配置不合法返回错误,由 bootstrap 降级为非联邦模式(不影响单站功能)。

func (*Node) Enabled added in v0.27.1

func (s *Node) Enabled() bool

Enabled 恒 true:实例仅在启用时被构造,handler 侧以 nil 判定未启用。

func (*Node) Healthy added in v0.27.1

func (s *Node) Healthy() bool

Healthy 最近一次 registry 心跳是否成功(未启动/尚未跑首个周期时为 false)。 供管理面(MCP federation_status 等)展示联邦健康度。

func (*Node) NodeID added in v0.27.1

func (s *Node) NodeID() string

NodeID 本节点联邦身份(Ed25519 公钥 hex)。

func (*Node) RegistryURLs added in v0.27.1

func (s *Node) RegistryURLs() []string

RegistryURLs 主备 registry 基址列表(配置逗号分隔项的副本)。

func (*Node) Resolve added in v0.27.1

func (s *Node) Resolve(code string) (*ResolveInfo, error)

Resolve 转查 registry(读路径依次主→备,首个命中即返回)。 全部未接入返回 (nil, nil)——调用方(resolve 代理 handler)以 available:false 回给前端,不区分"未启用/不存在"(降探测面)。

func (*Node) ShareCreated added in v0.27.1

func (s *Node) ShareCreated(code string, expiresAt *time.Time)

ShareCreated share 生命周期钩子:新分享公告到联邦(异步,registry 抖动 不拖慢建分享;失败由心跳循环补发)。熵门槛不过则静默跳过——短数字码 不出站,防 registry 侧在线枚举(安全模型见 p2p README)。

func (*Node) ShareDeleted added in v0.27.1

func (s *Node) ShareDeleted(code string)

ShareDeleted share 生命周期钩子:撤销公告(best-effort,索引立即移除; 撤销失败时 registry 条目按公告 TTL 自动过期)。

func (*Node) Start added in v0.27.1

func (s *Node) Start()

Start 启动注册/心跳循环(daemon;与 core 其他后台任务同约定,随进程退出)。

GoSafeLabeled:裸 goroutine 的 panic 无人接 ⇒ 带走整个进程(长驻服务尤其致命)。 轮次内的 panic 已由 loop() 里的 RunSafeLabeled 收编;这里再收编外壳, panic 经全局 async.SetPanicHandler 落结构化日志并带任务标签。

func (*Node) Stop added in v0.27.1

func (s *Node) Stop()

Stop 停止心跳循环并注销节点(best-effort;测试与优雅停机用)。

type PeerDirectory added in v0.27.0

type PeerDirectory struct {
	// contains filtered or unexported fields
}

PeerDirectory 联邦节点名录客户端。

func NewPeerDirectory added in v0.27.0

func NewPeerDirectory(provider RegistryURLProvider) *PeerDirectory

NewPeerDirectory 构造名录客户端(provider 由宿主注入;须非 nil)。

func (*PeerDirectory) Peers added in v0.27.0

func (d *PeerDirectory) Peers(ctx context.Context) ([]DirectoryPeer, error)

Peers 拉取在线名录(admin 目录对端选择器消费)。 直传未启用/二进制缺失不挡名录查询——名录可见性只取决于 registry 可达。

type RegistryURLProvider added in v0.27.0

type RegistryURLProvider func(ctx context.Context) (string, bool)

RegistryURLProvider 注册中心基址提供者;ok=false 表示该来源不提供值 (段不存在 / 值为空)。

由宿主注入(配置读取面在本包之外——app 域不得互 import,配置读取在 bootstrap 装配层),本包只认"基址字符串"。

type ResolveInfo

type ResolveInfo struct {
	NodeID    string    `json:"node_id"`
	URL       string    `json:"url"`
	Name      string    `json:"name"`
	ExpiresAt time.Time `json:"expires_at"`
	SizeHint  int64     `json:"size_hint"`
}

ResolveInfo 解析结果:口令在联邦内的源节点。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL