跳转至

帮忙 pin 文件站的 IPFS 镜像

anoni.net 文件站除了主站,还有一份 IPFS 镜像,让文件在主站被封锁或下架时仍然读得到。IPFS 上的内容要有节点 pin 才会存活,目前提供文件站内容的节点很少,社群自己那台是主力。你多架设一个节点帮忙 pin,网络上就多一份完整副本,抗删除的底气更足。

这页教你用一个常驻的 IPFS 节点,加一支自动跟上最新版本的定时脚本。整个过程对 Windows、Linux、macOS 都适用,有没有用 Docker 都可以。

要帮忙,你需要

  • 一台几乎整天开着、能联网的电脑(台式机、家用服务器、或一台小主机都可以)。
  • 安装 IPFS(下面会带你装)。
  • 执行一支定时脚本,每隔几小时自动 pin 最新版本。

帮不了节点也没关系,文末的「不想自架节点」有用 pinning service 代 pin 的替代做法。

为什么 CID 每次都不一样(IPFS 30 秒入门)

第一次接触 IPFS 的话,先理解一件事,后面的定时才有道理。IPFS 的深入设计见 去中心化网站发布,这里只说明到够用为止。

  • IPFS 把文件内容算出一段 hash,叫做 CID(Content Identifier),这段 CID 就是内容在 IPFS 上的地址。内容相同,CID 就相同。
  • 内容一改,CID 就跟着变。文件站每次更新、重新发布,都会产生一个全新的 CID。
  • IPNS(InterPlanetary Name System)是一个固定不变的名字,永远指向「目前最新」的那个 CID。文件站的 IPNS 名字就是下面那串 k51…
  • pin 的意思是「保证留住某个 CID 的内容」。pin 绑在 CID 上,不会自己跟着 IPNS 走。所以文件站一发新版,你上次 pin 的还是旧 CID。

结论就是这支脚本要做的事:先把 IPNS 换算成当前的 CID,再 pin 那个新 CID,然后放掉旧的。因为 CID 会变,换算与 pin 要定期重新执行,所以需要定时任务。

三串标识符不要混用

IPFS 上有三种长相接近的标识符,用途各自不同,贴错字段时工具会直接报错。

标识符 开头 会不会变 用途
CID bafybei… 每次发布都变 指定某一版内容
IPNS 名称 k51qzi… 固定 取得最新版,本页 pin 用这个
Peer ID 12D3KooW… 固定 指定要连到哪一个节点

pin 文件站只会用到 IPNS 名称。Peer ID 要到想让自己的节点跟社群节点保持固定连接时才用得上,做法见下面的〈进阶:跟社群节点保持常连〉。

运作原理(为什么定时就够,不用等通知)

你不需要社群通知你「换新版了」。IPNS 就是大家共用的同步点,你的脚本自己去解析就拿得到最新 CID。文件站发布时只是照常更新 IPNS,你这边每隔几小时解析一次、发现变了就 pin 新版,全程不用人工协调。

文件站的 IPFS 坐标(公开值,可以直接用):

  • IPNS 名称:k51qzi5uqu5dlfm2jj0f70ex3r3babmwy8qh071inwknttr7wqa3uhdwvlmrmw
  • 节点 Peer ID:12D3KooWEzvBhnLa6NZnjnw22Yoqs56xq4pNCZdkkxw5yxvi1eV9(设定 peering 时才会用到)
  • 浏览器打开看:https://anoni-net.ipns.dweb.link/

脚本每次执行的动作是:解析 IPNS 取得当前 CID,pin 新 CID,unpin 上次那版,回收空间。脚本先确认新版 pin 成功,才会放掉旧版。万一解析失败或抓不到内容,它会保留你手上现有的副本,不会让你的节点变空。

步骤一:架设一个常驻的 IPFS 节点

pin 要能抓齐内容,本机就得有一个持续运作的 IPFS daemon。下面依你的环境选一种。

安装 kubo(IPFS 官方的命令行版本)。macOS 可以用 Homebrew:

brew install kubo

Homebrew 的包名已从 ipfs 改为 kubo,旧名还是装得起来,但后面设定服务要用 kubo 这个名字。Intel 与 Apple Silicon 都支持。

Linux 依 官方说明 下载对应架构的 kubo。装好后初始化并启动 daemon:

ipfs init          # 第一次才需要
ipfs daemon

要让 daemon 长时间常驻,Linux 建议做成 systemd user service。macOS 用 Homebrew 安装的话,包本身附了服务定义,一行就会常驻并在登录时自动启动:

brew services start kubo

临时测试时,直接让 ipfs daemon 在后台执行也可以。

最简便的是安装 IPFS Desktop,它内含 kubo,登录后会自动在系统托盘常驻,daemon 一直开着。

装好后确认命令行能调用到 ipfs。独立版 kubo 需要自己把 ipfs.exe 加进系统 PATH,用 IPFS Desktop 的话,若 PATH 找不到 ipfs,在脚本里改用完整路径即可。

用一个 docker-compose.yml 执行 kubo,容器名取 ipfs_host

services:
  ipfs_host:
    image: ipfs/kubo:latest
    restart: always
    volumes:
      - ./ipfs-data:/data/ipfs
    ports:
      - "4001:4001"   # swarm,对外开有助于连到其他节点

启动:

docker compose up -d

之后对 IPFS 下指令都透过容器,设一个环境变量让脚本自动套用(下一步会用到):

export IPFS_CMD="docker exec ipfs_host ipfs"

步骤二:取得 pin 脚本

脚本会自己解析 IPNS、pin 新版、unpin 旧版。IPNS 名字已经写死在里面,不用改。Docker 用户记得先设好上一步的 IPFS_CMD

Linux、macOS、Docker:anoni-pin.sh

下载 anoni-pin.sh

#!/usr/bin/env bash
#
# anoni-pin.sh — pin the latest IPFS mirror of the anoni.net docs site.
#
# Usage:
#   ./anoni-pin.sh          # uses the local `ipfs`
# Run it on a schedule (cron) every 6 hours or so. See the docs for setup.
#
# Docker users: route ipfs commands through the container
#   export IPFS_CMD="docker exec ipfs_host ipfs"
#
# Design: pin the new version first, only then unpin the old one. If resolving
# or fetching fails, keep the copy you already have — never leave the node empty.
#
set -eu

# The docs site's IPNS name (public value, same as DNSLink _dnslink.anoni.net)
IPNS="k51qzi5uqu5dlfm2jj0f70ex3r3babmwy8qh071inwknttr7wqa3uhdwvlmrmw"

# Override the ipfs command via IPFS_CMD (see the Docker note above)
IPFS="${IPFS_CMD:-ipfs}"

# Remembers the last CID we pinned, so we can unpin it next time
STATE="${ANONI_PIN_STATE:-${HOME}/.anoni-pin/last_cid}"
mkdir -p "$(dirname "$STATE")"

# 1. Resolve the IPNS name to the current CID (--nocache forces a fresh lookup)
NEW="$($IPFS name resolve --nocache "/ipns/$IPNS" 2>/dev/null || true)"
if [ -z "$NEW" ]; then
    echo "[anoni-pin] resolve failed (network or node issue), keeping current state"
    exit 0
fi

OLD="$(cat "$STATE" 2>/dev/null || true)"
if [ "$NEW" = "$OLD" ]; then
    echo "[anoni-pin] already on the latest version $NEW, nothing to do"
    exit 0
fi

# 2. Pin the new version first (fetches the whole thing). With set -e, a failure
#    here aborts the script before we unpin anything.
echo "[anoni-pin] pinning new version: $NEW"
$IPFS pin add "$NEW"

# 3. Record state, unpin the old version, reclaim space
echo "$NEW" > "$STATE"
if [ -n "$OLD" ] && [ "$OLD" != "$NEW" ]; then
    echo "[anoni-pin] unpinning old version: $OLD"
    $IPFS pin rm "$OLD" || true
fi
$IPFS repo gc >/dev/null 2>&1 || true
echo "[anoni-pin] done, now pinning $NEW"

保存后给执行权限:

chmod +x anoni-pin.sh
./anoni-pin.sh          # 先手动跑一次确认正常

Windows:anoni-pin.ps1

下载 anoni-pin.ps1

#
# anoni-pin.ps1 — pin the latest IPFS mirror of the anoni.net docs site (Windows / PowerShell).
#
# Usage: make sure an IPFS daemon (IPFS Desktop or kubo) is running, then
#   powershell -NoProfile -ExecutionPolicy Bypass -File .\anoni-pin.ps1
# Run it on a schedule with Task Scheduler every 6 hours or so. See the docs.
#
# Docker users:
#   $env:IPFS_CMD = "docker exec ipfs_host ipfs"
#
# Design: pin the new version first, only then unpin the old one. If resolving
# or fetching fails, keep the copy you already have.
#

# The docs site's IPNS name (public value, same as DNSLink _dnslink.anoni.net)
$IPNS = 'k51qzi5uqu5dlfm2jj0f70ex3r3babmwy8qh071inwknttr7wqa3uhdwvlmrmw'

# Remembers the last CID we pinned, so we can unpin it next time
$State = if ($env:ANONI_PIN_STATE) { $env:ANONI_PIN_STATE } else { Join-Path $env:LOCALAPPDATA 'anoni-pin\last_cid.txt' }
New-Item -ItemType Directory -Force -Path (Split-Path $State) | Out-Null

# Override the ipfs command via IPFS_CMD (see the Docker note). Split into exe + prefix args.
$cmd   = if ($env:IPFS_CMD) { $env:IPFS_CMD } else { 'ipfs' }
$parts = $cmd -split '\s+'
$exe   = $parts[0]
$pre   = @()
if ($parts.Length -gt 1) { $pre = $parts[1..($parts.Length - 1)] }
function Invoke-Ipfs { & $exe @pre @args }

# 1. Resolve the IPNS name to the current CID (--nocache forces a fresh lookup)
$New = (Invoke-Ipfs name resolve --nocache "/ipns/$IPNS" 2>$null | Out-String).Trim()
if ([string]::IsNullOrWhiteSpace($New)) {
    Write-Host '[anoni-pin] resolve failed (network or node issue), keeping current state'
    exit 0
}

$Old = if (Test-Path $State) { (Get-Content $State -Raw).Trim() } else { '' }
if ($New -eq $Old) {
    Write-Host "[anoni-pin] already on the latest version $New, nothing to do"
    exit 0
}

# 2. Pin the new version first. Check LASTEXITCODE (not exceptions); on failure keep the old version.
Write-Host "[anoni-pin] pinning new version: $New"
Invoke-Ipfs pin add $New
if ($LASTEXITCODE -ne 0) {
    Write-Host '[anoni-pin] pin add failed, keeping the old version'
    exit 1
}

# 3. Record state, unpin the old version, reclaim space
Set-Content -Path $State -Value $New -NoNewline
if ($Old -and $Old -ne $New) {
    Write-Host "[anoni-pin] unpinning old version: $Old"
    Invoke-Ipfs pin rm $Old 2>$null
}
Invoke-Ipfs repo gc 2>$null | Out-Null
Write-Host "[anoni-pin] done, now pinning $New"

先手动执行一次确认正常(PowerShell 默认会挡脚本,用 -ExecutionPolicy Bypass 放行这一次):

powershell -NoProfile -ExecutionPolicy Bypass -File .\anoni-pin.ps1

步骤三:设定定时任务

让脚本每隔几小时自动执行。文件站更新不频繁,每 6 小时足够,想更快跟上可以缩短。

用 cron。编辑定时表:

crontab -e

加一行,每 6 小时执行一次,并把输出写进 log:

0 */6 * * * /path/to/anoni-pin.sh >> $HOME/.anoni-pin/log 2>&1

想要开机补执行漏掉的任务,也可以改用 systemd timer(设 Persistent=true),效果更稳。

用「任务计划程序」。最快的方式是命令行创建,每 6 小时执行一次:

schtasks /Create /TN "anoni-ipfs-pin" `
  /TR "powershell -NoProfile -ExecutionPolicy Bypass -File C:\Tools\anoni-pin.ps1" `
  /SC HOURLY /MO 6 /RL LIMITED

C:\Tools\anoni-pin.ps1 换成你实际存放的路径。也可以用「任务计划程序」的图形界面,触发程序填 powershell,参数填 -NoProfile -ExecutionPolicy Bypass -File 你的路径

定时任务照你主机的操作系统设(Linux、macOS 用 cron,Windows 用任务计划程序,见前两个分页)。差别只在脚本执行前要先设好 IPFS_CMD,例如包一层小 wrapper:

#!/usr/bin/env bash
export IPFS_CMD="docker exec ipfs_host ipfs"
exec /path/to/anoni-pin.sh

把 cron 指到这支 wrapper 即可。

步骤四:验证

确认脚本真的把内容 pin 住了。先解析出当前 CID,再看它在不在 pin 列表:

CID=$(ipfs name resolve --nocache /ipns/k51qzi5uqu5dlfm2jj0f70ex3r3babmwy8qh071inwknttr7wqa3uhdwvlmrmw)
ipfs pin ls --type=recursive | grep "${CID#/ipfs/}"

也可以在本机 gateway 打开看,内容正常显示就成功了:http://127.0.0.1:8080${CID}/。Docker 用户把上面的 ipfs 换成 docker exec ipfs_host ipfs

进阶:跟社群节点保持常连(选用)

pin 只靠 IPNS 名称就能完成,内容交给 DHT 去找。第一次要把整份镜像抓齐,碰上 DHT 查询慢或查不到的时候会拖很久。想让自己的节点跟来源节点维持固定连接,可以设定 kubo 的 peering。

编辑 ~/.ipfs/config,在最外层加入 Peering 区块:

"Peering": {
  "Peers": [
    { "ID": "12D3KooWEzvBhnLa6NZnjnw22Yoqs56xq4pNCZdkkxw5yxvi1eV9" }
  ]
}

Addrs 可以省略,kubo 会自己向 DHT 查地址。改完重启 daemon 生效。Docker 用户要改的是 ./ipfs-data/config,接着执行 docker compose restart

确认连上了:

ipfs swarm peers | grep 12D3KooWEzvBhnLa6NZnjnw22Yoqs56xq4pNCZdkkxw5yxvi1eV9

peering 是单向设定,社群节点那端没有对应条目,连接的保活责任落在你这边。kubo 官方文档 提醒过,单向 peering 会占用对方节点的连接资源,镜像数量变多之后负担会集中在同一台。建议实际遇到抓取不顺再开,平常运作正常就跳过。

维护与注意事项

  • 会自动跟上新版:文件站换 CID 后,下一次定时任务就会 pin 新版、放掉旧版,你不用做任何事。
  • 硬盘不会一直变大:脚本 unpin 旧版后会执行一次垃圾回收,只留最新版本占空间。一份完整镜像约 220 MB(2026 年 8 月实测)。
  • 不会弄丢你手上的版本:脚本先确认新版 pin 成功才放掉旧版,解析或下载失败时会保留现有副本。
  • 想停止帮忙:unpin 目前版本、把定时任务移除即可,不影响其他节点。
  • 隐私与风险:你 pin 的是公开文件,没有隐私顾虑。提供 IPFS pin 在不同司法管辖下的风险不同,相关限制见 去中心化网站发布 的「已知限制与风险」。

不想自架节点:用 pinning service 代 pin

不想维护 daemon 与定时任务的话,可以改用 PinataStoracha 这类 pinning service。在它们的界面直接贴上当前 CID 手动 pin,或用它们的 API 写个定时任务喂新 CID,逻辑跟本页脚本一样(解析 IPNS 拿 CID,再送给服务 pin)。

差别在存活重新依赖第三方服务商,而不是你自己的节点。当备援可以,想真正分散化还是自架节点最直接。

相关阅读