外太空九号博客

最近,我在想办法将我的微型博客整合到当前网站里,所以我开始重新考虑 IndieWeb 所提倡的一些构想:与希望一切都通过服务器 API 和 JSON 响应来动态完成的 ActivityPub(Mastodon、Pleroma 等用于互联的协议)不同,IndieWeb 社区推荐的不少标准都支持从有正确标记的静态 HTML 文件中直接生成机器可读的网站源。IndieWeb 隐含地依赖的一大核心要素是 URI(统一资源标志符)的稳定性,或者从更高的角度来说,站点所有者对域名的控制。由于最近关于.ORG 域名的 闹剧,我逐渐意识到,一个域名昂贵到无法维护(或可能随时被扣押)的未来可能并不是遥不可及的。这会严重破坏整个 IndieWeb 赖以建立的前提,更不用说更常见的链接失效了。幸运的是,我觉得 IPFS(星际文件系统)有潜力能够解决这两个问题。

IPFS 速成班

好的,好的,我知道和一些类似的项目,例如 Dat 协议pingfs,甚至 Scruttlebutt,比起来,IPFS 的名称听起来非常不靠谱(相信我,我开始时也和你一样怀疑),而不少加密货币类创业公司将 IPFS 与各种首字母缩写词混杂在其营销资料中的事实更降低了它的可信度,但 IPFS 看起来的确是类似项目中最成熟且易于使用的。以下我对解释 IPFS 所做的尝试,其信息大部分来自 官方文档 和这一 讲座。如果你对进一步的实现细节感兴趣,这一 来自 IPFS Camp 2019 的专题讨论 是一个很好的起始点。

简单地说,一条网页链接只是指向某服务器上文件路径的一种花哨说法。就像一般的文件路径一样,服务器下线后,即使坐在同一房间的某人可能缓存了网页内容,该链接也无法被访问。在 IPFS 中,文件(或数据块)通过与其内容相应的加密哈希值作为地址,并以分布式的方式存储在所有用户群中。这意味着我们不需要中心化的设施来访问文件、可以简单地验证文件完整性、可以使用 P2P 共享来加快访问速度、以及以这种方式存储的文件内容是无法改变的。

无法更改文件内容相比我们所得到的好处来说似乎是一个相当昂贵的代价,但是就像计算机科学中的任何其他问题一样,这可以通过添加抽象层来解决。解决这一问题的 IPNS(星际域名系统)利用公钥加密来创建可以指向不同文件的不可变地址。IPNS 地址基本上就是某个公钥的哈希值。一次 IPNS 查找包括取回公钥本身、搜索具有相应的私钥签名的指针文件(一个包含 IPFS 地址的文件)、辨认最新的指针文件、以及重定向到正确的地址几个步骤。要利用 IPNS,用户首先要创建一个公私钥对,然后将公钥、想分享的文件和带有签名的指针文件上传到 IPFS 上。当需要更新时,用户只需要签署并上传新的指针文件就可以了。

IPFS 的不少方面都可以在过去的项目中看到踪影,例如 BitTorrent(P2P 共享)、Plan 9 下的 FossilVenti(一次写入的数据块和路径重定向)、和 git(哈希树/有向无环图)。但是,IPFS 的杀手级功能在于其与现有架构集成的便捷程度。专用的 HTTP 网关允许从浏览器(而不是 IPFS 客户端)中直接访问 IPFS 或 IPNS 地址,而且 IPFS 还具有与 FUSE(用户空间文件系统)的兼容性,这意味着我们甚至可以将整个 IPFS 挂载为一个只读分区:这一兼容性也让我们能够托管静态网站,但是我必须承认,访问全球(甚至星际)规模的 P2P 共享盘是个明显更酷的用法。

在 IPFS 上架设静态网站

官方指南 已经很好地概述了使用方法。以下是简要概括:

完成此操作后,我们就可以从任何专用 HTTP 网关使用 <网关地址>/ipfs/<网站根目录哈希><网关地址>/ipns/<ipns-地址> 来访问刚才上传的网站了:我们可以使用由 IPFS 守护进程启动的本地网关(通常位于 127.0.0.1:8080),也可以使用 公共网关(由于 IPFS 文件取回需要在运行网关的服务器上进行,因此使用公共网关有遭受来自服务器所有者的中间人攻击的额外风险)。如果想要架设多个网站,则可以使用 ipns key gen 来生成更多 IPNS 密钥对,并在执行 ipfs name publish 时通过 --key 选项指定发布地址。

在 IPFS支持 IPNS 密钥的导入/导出 之前(这有助于我们备份密钥并从多台设备发布内容),DNSLink 可用于更方便地访问站点,但代价是需要拥有域名并信任 DNS 服务提供者。要想通过 /ipns/<域名> 从 HTTP 网关访问站点,只需为域名加入以下 TXT 记录:

dnslink=/ipfs/<网站根目录哈希>

dnslink=/ipns/<ipns-地址>

例如本站就可以通过 /ipns/shimmy1996.com(该链接使用 ipfs.io 架设的公共网关)来访问。虽然算不上是一个完全没有缺点的办法,但对我来说这是个合理的妥协。我发现 IPFS 通常比 IPNS 快,所以在 DNSLink 里用 IPFS 地址应该更加合适。为了避免每次手动复制粘贴,我在博客构建脚本中添加了以下内容以自动将网站上传到 IPFS 并更新 DNS 记录(使用 DigitalOcean 的 API):

echo "上传网站至 IPFS..."
hash=$(/usr/bin/ipfs add -Qr "<网站根目录>")

echo "更新 DNSLink 记录..."
token="<digitalocean-api-令牌>"
curl -X PUT \
     -H "Content-Type: application/json" \
     -H "Authorization: Bearer $token" \
     -d "{\"data\":\"dnslink=/ipfs/$hash\"}" \
     "https://api.digitalocean.com/v2/domains/<域名>/records/<记录-id>"

DigitalOcean 上 DNS 记录的记录 ID 也可以通过 其 API 取回,不过你可能需要在请求中增加 ?page=2 或更后面的页码才能找到你想要更新的记录。

对了,还需要注意的是,正如同使用任何脱机 HTML 文件时一样,我们需要在生成的网页中使用相对链接。在 Hugo 中,这可以通过在 config.toml 中加入

relativeURLs = true

来实现。

当然,作为一个 P2P 网络,IPFS 无法取回已经不存在于任何节点上的文件。默认情况下,IPFS 客户端会 固定 从本地计算机共享的任何内容:固定内容不会被删除,这确保 IPFS 上至少有一个可用的副本。我们可以取消固定网站的过时版本,或者,如果需要,在多台设备上查找并固定网站地址以防万一。

繁星若尘

回到 IndieWeb 的问题上:越来越黑的域名系统和链接失效使基于 HTTP 的 URI 的稳定性难以保证。但是,如果我们使用 IPFS 或 IPNS 地址作为 URI 呢?简直完美!我们通过由数学而非 FBI 警告所控制的地址获得了(理论上可以永久持续下去的)对静态网页的稳定分布式访问。消除拥有服务器的需要还降低了拥有个人网站的门槛。HTTP 协议已经存在了 29 年,而 IPFS 仅存在了 5 年。我不知道 IPFS 在接下来的 24 年中是否还会继续存在,但是如果是的话,我希望我们会看到一个或许更加混乱,但更加健壮、充满活力、多彩的在线世界。

评论区




暂时没有评论。