在去中心化存储领域,IPFS(InterPlanetary File System)无疑是最知名的项目之一。然而,对于开发者而言,如何在 Go 语言中构建 IPFS 应用一直是个挑战——直到 Boxo 的出现。
Boxo 是 IPFS 官方推出的 Go 语言组件库(SDK),它将 Kubo(原 go-ipfs)中经过生产验证的核心组件提取出来,使开发者能够轻松构建自己的 IPFS 实现和应用程序。
"The goal of this repo is to help people build things." — Boxo 官方Boxo 不是一个独立的 IPFS 实现,而是一个组件工具箱。它包含:
在 Boxo 出现之前,开发者面临以下困境:
| 问题 | 描述 |
|---|---|
| **代码难找** | 有用的 IPFS 代码散落在多个仓库中 |
| **使用困难** | 即使找到了代码,也不知道如何正确使用 |
| **被迫妥协** | 许多人只能运行 Kubo,通过 HTTP RPC API 交互 |
Boxo 的解决方案:将 Kubo 中的核心组件提取、整理、文档化,让开发者可以直接引用,而非绕道 HTTP API。
┌─────────────────────────────────────────────────────────────┐
│ IPFS 生态系统 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 应用层 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │ Kubo │ │ Lotus │ │ Rainbow │ │ Your App │ │
│ │(完整节点)│ │(Filecoin)│ │(Gateway)│ │(自定义应用) │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └──────┬──────┘ │
│ │ │ │ │ │
│ └────────────┴─────┬──────┴──────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Boxo SDK │ │
│ │ bitswap │ blockstore │ gateway │ ipns │ routing │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ go-libp2p │ │
│ │ (底层 P2P 网络库) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
重要澄清:Boxo 不是 IPFS 的"官方 Go 实现",而是多个可能的工具箱之一。它是 Kubo 团队维护的工具箱,但开发者完全可以选择其他路径构建 IPFS 应用。
Boxo 采用清晰的分层架构:
┌─────────────────────────────────────────────────────────────┐
│ 访问层 (Access) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Gateway │ │ MFS │ │ IPNS/NameSys │ │
│ │ (HTTP网关) │ │ (可变文件系统)│ │ (命名系统) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 数据结构层 (IPLD) │
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
│ │ MerkleDAG │ │ UnixFS │ │
│ │ (默克尔有向无环图) │ │ (文件/目录抽象) │ │
│ └─────────────────────┘ └─────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 存储层 (Storage) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │BlockService │ │ Blockstore │ │ Filestore │ │
│ │ (统一块服务)│ │ (块存储) │ │ (零拷贝文件存储) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 内容交换层 (Exchange) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Bitswap │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ Client │ │ Server │ │ Network │ │ │
│ │ │ (客户端) │ │ (服务端) │ │ (P2P/HTTP网络) │ │ │
│ │ └──────────┘ └──────────┘ └──────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 路由与发现层 (Routing) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ DHT │ │ Provider │ │ Delegated Routing │ │
│ │ (分布式哈希)│ │ (内容提供者)│ │ (委托路由) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Bitswap 是 IPFS 的核心数据交换协议,负责在节点之间请求和发送数据块。
┌─────────────────────────────────────────────────────────────┐
│ Bitswap 协议版本 │
├─────────────────────────────────────────────────────────────┤
│ │
│ /ipfs/bitswap/1.0.0 → 初始版本 │
│ /ipfs/bitswap/1.1.0 → 支持 CIDv1 │
│ /ipfs/bitswap/1.2.0 → 支持 Want-Have 和 Have/DontHave │
│ │
└─────────────────────────────────────────────────────────────┘
消息类型:
| 消息类型 | 方向 | 说明 |
|---|---|---|
want-have | 客户端→服务端 | 询问对方是否有某个块 |
want-block | 客户端→服务端 | 请求实际的块数据 |
HAVE | 服务端→客户端 | 确认拥有该块 |
DONT_HAVE | 服务端→客户端 | 确认没有该块 |
Cancel | 客户端→服务端 | 取消请求 |
关键特性:
HAVE 响应、被发现为提供者、或最先发送数据的节点want-have,并查询 DHTimport (
"context"
bitswap "github.com/ipfs/boxo/bitswap"
bsnet "github.com/ipfs/boxo/bitswap/network/bsnet"
)
// 创建 Bitswap 网络
network := bsnet.NewFromIpfsHost(host, router)
// 创建 Bitswap 交换器
exchange := bitswap.New(ctx, network, bstore)
// 获取单个块
block, err := exchange.GetBlock(ctx, cid)
// 获取多个块(异步)
blockChan, err := exchange.GetBlocks(ctx, cids)
// 使用会话优化相关请求
session := exchange.NewSession(ctx)
blocksChan, err := session.GetBlocks(ctx, relatedCids)
Blockstore 提供内容寻址的块存储接口:
import "github.com/ipfs/boxo/blockstore"
// 基础块存储
bs := blockstore.NewBlockstore(datastore)
// 带 Bloom Filter 缓存
cachedBs := blockstore.NewBloomCached(bs, bloomFilter)
// 带 ARC(自适应替换缓存)
arcBs := blockstore.NewArcCached(bs, size)
// 验证块存储(读取时验证哈希)
validatedBs := blockstore.NewValidatingBlockstore(bs, verifcid.DefaultAllowlist)
Filestore 扩展:零拷贝存储,直接从原始文件读取块数据,避免数据重复:
import "github.com/ipfs/boxo/filestore"
fm := filestore.NewFileManager(datastore, datastore, filepath)
fs := filestore.New(cs, fm, nil)
Gateway 模块实现了 IPFS HTTP 网关规范,支持通过 HTTP 访问 IPFS 内容。
网关类型:
| 类型 | URL 格式 | 说明 |
|---|---|---|
| **路径网关** | http://gateway/ipfs/{cid}/path | 最简单,但有安全限制 |
| **子域名网关** | http://{cid}.ipfs.gateway/path | 安全隔离,推荐生产使用 |
| **DNSLink 网关** | http://dnslink.tld/path | 使用域名映射到 IPNS |
配置选项:
import "github.com/ipfs/boxo/gateway"
conf := gateway.Config{
// 支持反序列化响应(dag-json, dag-cbor 等)
DeserializedResponses: true,
// 允许编解码器转换
AllowCodecConversion: false,
// 内容检索超时(默认 30 秒)
RetrievalTimeout: 30 * time.Second,
// 最大并发请求数(默认 4096)
MaxConcurrentRequests: 4096,
// 请求总超时(默认 1 小时)
MaxRequestDuration: time.Hour,
}
创建网关:
// 创建后端实现
backend, err := gateway.NewBlocksBackend(blockService)
// 创建处理器
handler := gateway.NewHandler(conf, backend)
// 添加 CORS 支持
handler = gateway.NewHeaders(nil).ApplyCors().Wrap(handler)
// 挂载路由
mux := http.NewServeMux()
mux.Handle("/ipfs/", handler)
mux.Handle("/ipns/", handler)
支持的响应格式:
| 格式 | Accept Header | 说明 |
|---|---|---|
| Raw | application/vnd.ipld.raw | 原始块数据 |
| CAR | application/vnd.ipld.car | 内容存档格式 |
| DAG-JSON | application/vnd.ipld.dag-json | JSON 格式 DAG |
| DAG-CBOR | application/vnd.ipld.dag-cbor | CBOR 格式 DAG |
| HTML | text/html | 目录列表/文件预览 |
IPNS(InterPlanetary Naming System) 提供可变指针,指向不可变的 IPFS 内容。
┌─────────────────────────────────────────────────────────────┐
│ IPNS 记录结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Value → 内容路径 (/ipfs/bafy..., /ipns/...) │
│ ValidityType → 有效期类型(仅支持 EOL) │
│ Validity → 过期时间(RFC3339 格式) │
│ Sequence → 版本号(从 0 开始递增) │
│ TTL → 缓存提示(纳秒) │
│ PublicKey → 验证公钥(Ed25519 可选) │
│ Signature → 签名(signatureV2) │
│ │
└─────────────────────────────────────────────────────────────┘
支持的密钥类型:
| 类型 | 支持级别 | 说明 |
|---|---|---|
| Ed25519 | MUST | 默认,密钥可内联到 IPNS 名称 |
| RSA | SHOULD | 遗留兼容 |
| Secp256k1, ECDSA | MAY | 私有用途 |
使用示例:
import "github.com/ipfs/boxo/ipns"
// 创建 IPNS 记录
record, err := ipns.NewRecord(privateKey, path, sequence, eol, ttl)
// 验证记录
err := ipns.Validate(record, ipnsName)
// 使用公钥验证
valid, err := ipns.ValidateWithKey(record, pubKey)
// 嵌入数据到记录
embedded := ipns.EmbedPublicKey(publicKey, record)
NameSys(命名系统):
import "github.com/ipfs/boxo/namesys"
// 创建解析器
resolver := namesys.NewNameSystem(routing, ds, opts...)
// 解析 IPNS 路径
path, ttl, err := resolver.Resolve(ctx, ipnsPath)
// 发布 IPNS 记录
err := resolver.Publish(ctx, privateKey, value, opts...)
Boxo 提供多种路由实现:
HTTP Delegated Routing:
import "github.com/ipfs/boxo/routing/http/client"
// 创建委托路由客户端
client, err := client.New(
"https://delegated-ipfs.dev",
client.WithUserAgent("my-app/1.0"),
)
// 查找内容提供者
providers, err := client.FindProviders(ctx, cid)
// 查找节点
peers, err := client.FindPeer(ctx, peerID)
// 解析 IPNS
record, err := client.GetIPNS(ctx, ipnsName)
API 端点:
| 端点 | 说明 |
|---|---|
GET /routing/v1/providers/{cid} | 查找内容提供者 |
GET /routing/v1/peers/{peer-id} | 查找节点记录 |
GET /routing/v1/ipns/{name} | 解析 IPNS 记录 |
PUT /routing/v1/ipns/{name} | 发布 IPNS 记录 |
Boxo 采用了独特的发布策略,值得深入研究。
┌─────────────────────────────────────────────────────────────┐
│ Boxo 版本策略 │
├─────────────────────────────────────────────────────────────┤
│ │
│ • 仅发布 minor 和 patch 版本,无 major 版本变更计划 │
│ • 当前版本:v0.x.x(长期保持) │
│ • Minor 版本:新功能、移除功能、重大依赖变更 │
│ • Patch 版本:Bug 修复、安全修复 │
│ │
└─────────────────────────────────────────────────────────────┘
Boxo 将破坏性变更分为两类:
类型 1:API 重构/变更
"We believe there's a lot of innovation and growth for IPFS in the future, so we don't want Boxo to ossify."
Boxo 遵循 Go 官方策略:
┌─────────────────────────────────────────────────────────────┐
│ 发布触发条件 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 每次 Kubo 发布时(至少) │
│ 2. 按需发布(有重大变更时) │
│ 3. 可以在周五或周末发布(与 Kubo 不同) │
│ │
└─────────────────────────────────────────────────────────────┘
Boxo 维护了一个详细的发布清单:
- [ ] 验证 GPG 签名配置
- [ ] 确保本地有 Boxo 和 Kubo 代码
- [ ] 创建 release-vX.Y.Z 分支
- [ ] 整理 CHANGELOG
- [ ] 创建 Draft PR
- [ ] 确保 Boxo 测试通过
- [ ] 确保 Kubo 测试通过(需要升级 Boxo 依赖)
- [ ] 更新 version.json
- [ ] 添加 "release" 标签
- [ ] 检查 gorelease 警告
- [ ] 复制 CHANGELOG 到 GitHub Release
- [ ] 等待维护者批准
- [ ] 合并(使用 Merge Commit,保留分支)
- [ ] 在 Discourse 公告
- [ ] 更新 Kubo PR 并合并
// 新增 DAG 宽度控制
import "github.com/ipfs/boxo/ipld/unixfs/io"
// UnixFS Profile 预设
profile := io.UnixFS_v1_2025 // 2025 年标准
// 或
profile := io.UnixFS_v0_2015 // 2015 年兼容模式
// 配置 HAMT 分片阈值
mode := io.SizeEstimationBlock // 基于块大小
// 或
mode := io.SizeEstimationLinks // 基于链接数(遗留)
IPIP-523:?format= 查询参数优先于 Accept 头
# 之前:Accept 头优先
curl -H "Accept: application/json" "http://gateway/ipfs/cid?format=raw"
# 返回 JSON
# 现在:?format= 优先
curl -H "Accept: application/json" "http://gateway/ipfs/cid?format=raw"
# 返回 Raw 块
IPIP-524:禁用默认编解码器转换
# 请求 dag-json 格式,但块是 dag-pb
# 之前:自动转换为 dag-json
# 现在:返回 406 Not Acceptable,建议客户端获取 raw 后自行转换
┌─────────────────────────────────────────────────────────────┐
│ 块大小限制变更 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 之前:1 MiB │
│ 现在:2 MiB(匹配 Bitswap 规范) │
│ │
│ chunker 默认大小:2 MiB - 256 bytes(为 protobuf 留空间) │
│ │
└─────────────────────────────────────────────────────────────┘
package main
import (
"context"
"log"
"net/http"
"os"
"github.com/ipfs/boxo/blockservice"
"github.com/ipfs/boxo/gateway"
offline "github.com/ipfs/boxo/exchange/offline"
carblockstore "github.com/ipld/go-car/v2/blockstore"
)
func main() {
ctx := context.Background()
// 打开 CAR 文件
f, err := os.Open("data.car")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// 创建只读 CAR blockstore
bs, err := carblockstore.NewReadOnly(f, nil)
if err != nil {
log.Fatal(err)
}
// 创建 blockservice(离线模式,因为数据都在 CAR 中)
blockService := blockservice.New(bs, offline.Exchange(bs))
// 创建网关后端
backend, err := gateway.NewBlocksBackend(blockService)
if err != nil {
log.Fatal(err)
}
// 配置网关
conf := gateway.Config{
DeserializedResponses: true,
}
// 创建处理器
handler := gateway.NewHandler(conf, backend)
// 启动服务
log.Println("Gateway listening on :8080")
log.Fatal(http.ListenAndServe(":8080", handler))
}
package main
import (
"context"
"log"
bsclient "github.com/ipfs/boxo/bitswap/client"
bsnet "github.com/ipfs/boxo/bitswap/network/bsnet"
"github.com/ipfs/boxo/blockservice"
"github.com/ipfs/boxo/blockstore"
"github.com/ipfs/boxo/ipld/merkledag"
"github.com/ipfs/boxo/ipld/unixfs"
"github.com/ipfs/go-datastore"
dsync "github.com/ipfs/go-datastore/sync"
"github.com/libp2p/go-libp2p"
dht "github.com/libp2p/go-libp2p-kad-dht"
)
func main() {
ctx := context.Background()
// 1. 创建 libp2p 主机
h, err := libp2p.New()
if err != nil {
log.Fatal(err)
}
log.Printf("Peer ID: %s", h.ID())
// 2. 创建 DHT
kademliaDHT, err := dht.New(ctx, h)
if err != nil {
log.Fatal(err)
}
// 3. 创建 blockstore
ds := dsync.MutexWrap(datastore.NewMapDatastore())
bs := blockstore.NewBlockstore(ds)
// 4. 创建 Bitswap 网络
net := bsnet.NewFromIpfsHost(h, kademliaDHT)
// 5. 创建 Bitswap 客户端
client := bsclient.New(ctx, net, nil, bs)
net.Start(client)
defer client.Close()
// 6. 创建 blockservice
bserv := blockservice.New(bs, client)
// 7. 创建 DAG 服务
dagService := merkledag.NewDAGService(bserv)
// 8. 获取文件(假设已知 CID)
// cid, _ := cid.Decode("bafy...")
// node, err := dagService.Get(ctx, cid)
// fileNode, err := unixfs.GetNode(ctx, node)
}
| 特性 | Boxo (Go) | Helia (JavaScript) |
|---|---|---|
| **运行环境** | 原生二进制、服务器 | 浏览器、Node.js |
| **性能** | 高(编译型) | 中(解释型) |
| **Bitswap** | 完整实现 | 完整实现 |
| **Gateway** | 内置 | 需自行实现 |
| **使用场景** | 基础设施、后端服务 | Web 应用、浏览器扩展 |
| 特性 | Boxo SDK | Kubo HTTP API |
|---|---|---|
| **依赖** | 直接依赖 | 需要 Kubo 进程 |
| **延迟** | 低(进程内调用) | 高(HTTP 开销) |
| **灵活性** | 高(可自由组合) | 低(受限 API) |
| **运维** | 简单(单一进程) | 复杂(需管理 Kubo) |
| **功能** | 完整组件访问 | 仅暴露的 API |
┌─────────────────────────────────────────────────────────────┐
│ 方案选择指南 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 选择 Boxo 当: │
│ • 需要构建自定义 IPFS 实现 │
│ • 需要高性能、低延迟 │
│ • 需要细粒度控制组件 │
│ • 需要嵌入 IPFS 功能到现有应用 │
│ │
│ 选择 Kubo HTTP API 当: │
│ • 快速原型开发 │
│ • 不想管理依赖 │
│ • 只需要基础功能 │
│ • 跨语言调用 │
│ │
│ 选择 Helia 当: │
│ • 构建浏览器应用 │
│ • 需要 Node.js 环境 │
│ • 轻量级 P2P 通信 │
│ │
└─────────────────────────────────────────────────────────────┘
Boxo 已被多个重要项目采用:
| 项目 | 描述 | 使用场景 |
|---|---|---|
| **Kubo** | 最流行的 IPFS 实现 | 核心组件来源 |
| **Lotus** | Filecoin 实现 | 存储和检索 |
| **Rainbow** | 高性能 HTTP 网关 | 网关服务 |
| **ipfs-check** | 数据可用性检查工具 | 诊断工具 |
| **someguy** | 委托路由基础设施 | 路由服务 |
如果你有项目使用旧的独立仓库,Boxo 提供了迁移工具:
# 更新导入路径
go run github.com/ipfs/boxo/cmd/boxo-migrate@latest update-imports
# 检查未维护的依赖
go run github.com/ipfs/boxo/cmd/boxo-migrate@latest check-dependencies
主要仓库迁移映射:
| 旧仓库 | Boxo 包 |
|---|---|
github.com/ipfs/go-bitswap | boxo/bitswap |
github.com/ipfs/go-blockservice | boxo/blockservice |
github.com/ipfs/go-ipns | boxo/ipns |
github.com/ipfs/go-namesys | boxo/namesys |
github.com/ipfs/go-merkledag | boxo/ipld/merkledag |
github.com/ipfs/go-unixfs | boxo/ipld/unixfs |
github.com/ipfs/go-path | boxo/path |
github.com/ipfs/go-mfs | boxo/mfs |
github.com/ipfs/go-ipfs-provider | boxo/provider |
github.com/ipfs/go-ipfs-pinner | boxo/pinning/pinner |
Boxo 是 IPFS 生态系统中的重要基础设施,它:
本文基于 Boxo v0.37.0(2025 年 2 月)撰写,项目持续演进中。
还没有人回复