运行 OpenFiat 节点
节点运营者维护位于区块链之上的去中心化市场。Solana 验证者负责保障链的安全并执行 OpenFiat 程序;节点则承载挂单、信誉、通讯与发现。
以下命令基于参考部署方案:服务名为 openfiat-node,配置位于 /etc/openfiat,数据位于 /var/lib/openfiat。
硬件要求
最低配置
4 个 CPU 核心 · 16 GB 内存 · 250 GB NVMe 固态硬盘 · 稳定的宽带连接
生产环境推荐配置
8–16 个 CPU 核心 · 32 GB 或更多内存 · 1 TB NVMe 固态硬盘 · 高带宽、低延迟网络 · UPS 电源保护 · 冗余互联网线路
节点是一个独立的 Rust 二进制文件。libp2p(QUIC、Noise、Yamux)与 RocksDB 已编译在内,无需单独安装。
在哪里运行节点
AllenHark 主导协议的早期开发,同时提供运行节点所需的两样东西:服务器与 Solana RPC 接入。作为 OpenFiat 计划的一部分,节点运营者可享两者 50% 的折扣。
Solana RPC 与 gRPC
提供质押连接,你的节点提交的交易不必与其他人排在同一队列中。接入通过 IP 白名单授权,而非 API 密钥。如需流式获取账户与区块槽更新,也提供 Yellowstone gRPC。
查看价格服务网络,而不只是服务自己
只做读取的节点是客人;能被公众访问的节点才是基础设施——钱包、浏览器和网页应用总要连上*某个人*,而今天这样的「某个人」实在太少。每一位把节点放到 TLS 之后并设置 --public-rpc-url 的运营者,都在扩大这个集合,这决定了网络究竟是拥有多个彼此独立的入口,还是所有人都默默依赖着同一个单点。代价不过是一张证书和一层反向代理,而且它是有回报的:可被访问的节点才能被对等节点挑战与奖励,OpenFiat 自己的应用也会和其他节点一样使用你的节点。
动手之前有两点值得了解。对公众提供服务意味着真实流量,请据此配置机器并关注 metrics 端点。另外,公开节点是一种公开承诺——人们会基于你发布的 URL 进行开发,因此若要下线,请有计划地进行,而不是悄然关停。
安装方式
预编译二进制——用于生产环境
提供适用于 x86-64 与 arm64 Linux 的静态构建。放入 /usr/local/bin,再交由 systemd 运行。这是运行一个供他人依赖的节点时受支持的方式:服务会在崩溃或重启后自动恢复,并有足够长的停机宽限期让数据库干净地落盘。
GitHub Releases 页面useradd --system --create-home --home-dir /var/lib/openfiat --shell /usr/sbin/nologin openfiat
install -m 755 openfiat-node /usr/local/bin/openfiat-node
mkdir -p /etc/openfiat
chown -R openfiat:openfiat /var/lib/openfiat
systemctl daemon-reload
systemctl enable --now openfiat-node
journalctl -u openfiat-node -fDocker——仅用于测试
每个版本都会发布官方镜像。可用于在本地试用节点,或搭建一个用完即弃的多节点集群——但不要用它运行网络所依赖的节点。该镜像的用途是可复现的本地测试,本页所记录的参考部署方案是上面的 systemd 方式。
# /opt/openfiat/compose.yaml
# TESTING ONLY. Use this to try a node locally or to bring up a throwaway
# cluster — run a production node from the binary under systemd instead
# (see the unit file further down this page).
services:
node:
image: ghcr.io/openfiat-org/openfiat-node:latest
restart: unless-stopped
# QUIC needs UDP; the same TCP port serves JSON-RPC, WebSocket, REST,
# health, and metrics together — there is no separate metrics port.
ports:
- "4001:4001/udp"
- "7080:7080/tcp"
volumes:
- /var/lib/openfiat:/data
- /etc/openfiat/wallet.json:/data/wallet.json:ro
command:
- --ledger=/data
- --identity=/data/wallet.json
- --rpc-bind-address=0.0.0.0:7080
- --gossip-bind-address=/ip4/0.0.0.0/udp/4001/quic-v1
# Static multiaddr — DNS bootstrap does not resolve (see this
# file's own top comment). Repeat the flag for several.
- --entrypoint=/ip4/84.32.223.111/udp/4001/quic-v1/p2p/12D3KooWK9hQ7TwbfvFiaAxUbRFCkdhS7iEpAJDnewNL1anyREQ1
# Omit to stay GossipOnly, the safe default. Any value opts into
# real Solana devnet connectivity (OFS-4300 §4).
- --solana-rpc-url=https://api.devnet.solana.com
stop_grace_period: 60s
ulimits:
nofile: 65536install -d -m 0750 /var/lib/openfiat
docker compose -f /opt/openfiat/compose.yaml up -d
docker compose -f /opt/openfiat/compose.yaml logs -f node从源码构建
需要 Rust 工具链,以及用于编译 RocksDB 的 C 工具链。适用于运行未发布的提交、为未支持的平台构建,或审计你所运行的代码。
# Rust toolchain, then build the node binary
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
apt install -y build-essential pkg-config libssl-dev clang cmake
git clone https://github.com/OpenFiat-org/openfiat-core.git
cd openfiat-core
cargo build --release --bin openfiat-node
install -m 0755 target/release/openfiat-node /usr/local/bin/端口
- 4001/UDP点对点流量。QUIC 是主要传输方式,因此该 UDP 端口必须可达——也是最容易被遗漏的端口。必须可达
- 7080/TCPJSON-RPC、WebSocket、REST、健康检查与指标共用同一个真实端口,并非各自独立的端口。必须可达
- 7080/TCP与上面的 API 同一端口(GET /metrics)。如不希望公开,请仅在回环地址或内网上开放。保持内部可见
让节点上线
请按顺序完成以下步骤,每一步都附有所需的命令。
- 01
准备服务器
一台较新的 Debian 或 Ubuntu 服务器、一个防火墙,以及准确的系统时钟。时钟比看起来更重要:记录都带有时间戳与有效期,时钟漂移会让节点拒绝本来有效的数据。
shell# Debian 12 / Ubuntu 24.04, as root apt update && apt install -y ca-certificates curl gnupg ufw chrony # Clock drift breaks signature and expiry checks — keep NTP running. systemctl enable --now chrony - 02
安装节点
如果这个节点会被他人依赖,请安装二进制文件并交由 systemd 运行——本手册后续步骤均以此为前提。在本地测试或搭建用完即弃的集群时可以使用 Docker;当你需要运行未发布的提交,或想审计自己实际运行的代码时,则从源码构建。
- 03
生成节点的钱包
并不存在单独的“节点身份”格式——节点的身份就是一个真实的 Solana CLI 格式 wallet.json,与 solana-keygen 生成的文件相同。它的种子会同时用作节点的 gossip/对等身份与 Solana 签名密钥。请务必保留该文件——一旦丢失,节点将以陌生身份重新加入,信誉也要从零重新积累。
shell# The node's identity IS a Solana CLI-format wallet.json — the same # file "solana-keygen new" produces (its own seed is reused for the # node's gossip/P2P keypair too, see crates/cli's load_or_generate_wallet). # There is no separate "node identity" format or command. solana-keygen new --outfile /etc/openfiat/wallet.json chmod 600 /etc/openfiat/wallet.json solana-keygen pubkey /etc/openfiat/wallet.json - 04
设置环境变量
openfiat-node 没有自己的配置文件——每一项设置都是启动时读取一次的环境变量:数据存放位置、监听地址、启动时拨号的对等节点,以及(可选的)使用哪个 Solana RPC 端点。引导节点必须是静态的 multiaddr/IP,而不是主机名——DNS 引导地址无法解析。
shell# There is no config file and no environment variables. Every # setting is a flag on the systemd unit's ExecStart below, so # "systemctl cat openfiat-node" shows exactly what a running node was # given, and "openfiat-node --help" is the whole surface. # # Try it in the foreground first — the node prints the addresses it is # reachable at and whether it reached Solana: openfiat-node \ --ledger /var/lib/openfiat \ --identity /etc/openfiat/wallet.json \ --rpc-bind-address 0.0.0.0:7080 \ --gossip-bind-address /ip4/0.0.0.0/udp/4001/quic-v1 \ --entrypoint /ip4/84.32.223.111/udp/4001/quic-v1/p2p/12D3KooWK9hQ7TwbfvFiaAxUbRFCkdhS7iEpAJDnewNL1anyREQ1 \ --solana-rpc-url https://api.devnet.solana.com # Omit --solana-rpc-url to stay GossipOnly, the safe default: the node # still serves the marketplace, but its on-chain answers come # second-hand from peers. Never put a real endpoint or API key anywhere # version controlled — it belongs in the unit file on the server. # # Add --ipfs-api-url http://127.0.0.1:5001 to pin protocol content and # earn the full reward share, and --retention archival to keep the whole # history instead of a rolling 30 days. - 05
开放必要端口
由于 QUIC 是主要传输方式,对等节点通过 UDP 访问节点——这也是最容易被遗漏的端口。JSON-RPC、WebSocket、REST、健康检查与指标共用同一个 TCP 端口;如不希望公开对外提供服务,请将其保持私有。
shellufw default deny incoming ufw allow 22/tcp # keep your own access ufw allow 4001/udp # libp2p, QUIC — the port people most often forget ufw allow 443/tcp # once nginx terminates TLS in front (see below) ufw allow 7080/tcp # only if the node is exposed directly, without a proxy ufw enable && ufw status verbose - 06
服务网络:让你的节点可被访问
这一步会把节点从旁观网络的角色,变成真正承载网络的一环。在前面架好 nginx 并申请证书,然后用 --public-rpc-url 告诉节点它的公开地址,它就会对外通告自己,让钱包、浏览器以及 OpenFiat 自己的网页应用都能使用它。顺序很关键:先用纯 HTTP 配好 nginx,再由 certbot 添加 TLS——一份已经指向证书文件的配置根本无法启动,certbot 会在签发那份证书之前就失败。证书并非可有可无的装饰:通过 HTTPS 提供的页面无法建立纯 HTTP 连接,因此没有证书的节点,无论多健康,对所有浏览器而言都是不可见的。
/etc/nginx/sites-available/openfiat-node# /etc/nginx/sites-available/openfiat-node # # HTTP only at this stage, with NO TLS directives. certbot --nginx works # by running "nginx -t" and editing this file, so a config that already # points at a certificate cannot load, nginx will not start, and certbot # fails before it can issue the certificate that would have fixed it. server { listen 80; listen [::]:80; server_name openfiat.example.com; location / { proxy_pass http://127.0.0.1:7080; proxy_http_version 1.1; # The node serves its WebSocket event stream on the same port; # without these the stream downgrades to a plain request and # subscriptions silently never deliver. proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Do NOT add CORS headers here — the node already sets them, and # two Access-Control-Allow-Origin headers on one response make # browsers reject it while curl sees a clean 200. proxy_read_timeout 300s; } }shell# The A record must already resolve for the world — certbot proves # control of the name over port 80. Check a public resolver, not your # own machine, whose cache can be stale: dig +short @8.8.8.8 openfiat.example.com ln -s /etc/nginx/sites-available/openfiat-node /etc/nginx/sites-enabled/ nginx -t && systemctl reload nginx curl -s http://openfiat.example.com/health # ok, before any certificate apt install -y certbot python3-certbot-nginx certbot --nginx -d openfiat.example.com # certbot edits the server block in place: it adds listen 443 ssl, the # certificate paths, and a redirect from :80. The location block and its # proxy headers carry over. Renewal installs itself as a systemd timer. # Do not hand-write a 443 block afterwards — certbot manages that one. # Then tell the network this node can be reached directly: # openfiat-node --public-rpc-url https://openfiat.example.com - 07
以系统服务方式运行
交由 systemd 管理后,节点在崩溃或重启后会自动恢复,并获得足够长的停机宽限时间,使数据库能够完整落盘,而不是在写入中途被强制终止。
openfiat-node.service# /etc/systemd/system/openfiat-node.service [Unit] Description=OpenFiat node Documentation=https://docs.openfiat.network After=network-online.target Wants=network-online.target [Service] Type=simple User=openfiat Group=openfiat ExecStart=/usr/local/bin/openfiat-node \ --ledger /var/lib/openfiat \ --identity /etc/openfiat/wallet.json \ --rpc-bind-address 0.0.0.0:7080 \ --gossip-bind-address /ip4/0.0.0.0/udp/4001/quic-v1 \ --entrypoint /ip4/84.32.223.111/udp/4001/quic-v1/p2p/12D3KooWK9hQ7TwbfvFiaAxUbRFCkdhS7iEpAJDnewNL1anyREQ1 \ --solana-rpc-url https://api.devnet.solana.com WorkingDirectory=/var/lib/openfiat Restart=on-failure RestartSec=5s # openfiat-node handles SIGTERM itself (graceful shutdown) rather than # needing systemd to force-kill it; this gives that shutdown path a # reasonable window before systemd escalates to SIGKILL. TimeoutStopSec=30s NoNewPrivileges=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/var/lib/openfiat PrivateTmp=true # AF_NETLINK is required, not optional: binding a wildcard address makes # libp2p enumerate this host's interfaces, and that goes over a netlink # socket. Without it the QUIC listener fails and the gossip actor panics # while the HTTP thread survives — so systemd reports the unit active # and the node looks healthy while serving nothing. RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK [Install] WantedBy=multi-user.targetshelluseradd --system --create-home --home-dir /var/lib/openfiat --shell /usr/sbin/nologin openfiat install -m 755 openfiat-node /usr/local/bin/openfiat-node mkdir -p /etc/openfiat chown -R openfiat:openfiat /var/lib/openfiat systemctl daemon-reload systemctl enable --now openfiat-node journalctl -u openfiat-node -f - 08
让它完成追赶
新节点不会重放全部历史,而是可以发现并导入对等节点公告的市场状态快照(OFS-1300)——这是真实的 JSON-RPC 方法,而非独立工具:getLatestSnapshot、getCheckpointHeight。签名、协议版本与状态根都必须匹配,才会被信任。
shell# Snapshot sync (OFS-1300) is real JSON-RPC, not a separate CLI — # a new node discovers and imports a peer-announced snapshot instead of # replaying all history: curl -s -X POST http://localhost:7080/rpc -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"getLatestSnapshot","params":{}}' curl -s -X POST http://localhost:7080/rpc -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"getCheckpointHeight","params":{}}' - 09
确认运行健康
GET /health 确认进程已启动;通过 JSON-RPC 调用 getChainStatus 可以看到它处于 GossipOnly 还是 RpcConnected,以及(若为后者)当前的区块哈希。
shell# Is it up, and which mode is it in? curl -s http://localhost:7080/health # ok curl -s -X POST http://localhost:7080/rpc -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"getChainStatus","params":{}}' # {"jsonrpc":"2.0","id":1,"result":{"mode":"GossipOnly","blockhash":null,"slot":null,"age_ms":null}} # ("RpcConnected" with a real blockhash once --solana-rpc-url is set) # The addresses your node is reachable at are in its own log — give one # of these to another operator as their --entrypoint: journalctl -u openfiat-node | grep "reachable at a new address" - 10
它已经是网络的一部分
并不存在单独的“注册”步骤——只要节点配置了引导节点,就会自动进行 gossip 广播与被广播,无需任何人批准。质押、发布服务注册元数据、加入争议仲裁与参与治理投票,都是客户端针对运行中节点执行的独立、由钱包驱动的操作——请参阅各自对应的参与指南。
节点需质押 OPEN 才能成为活跃参与者。但质押买不到信誉:表现不佳的运营者无法靠多质押来弥补。有效优先级由信誉、质押与实测网络表现共同决定。
- 11
持续监控
真正能预警问题的信号是:已连接对等节点数、链路模式(GossipOnly 还是 RpcConnected)与区块哈希的新鲜度。为这些配置告警,你就能比用户更早发现异常。
prometheus.yml# /etc/prometheus/prometheus.yml — same port as everything else, # no separate metrics listener to configure on the node's own side. scrape_configs: - job_name: openfiat-node static_configs: - targets: ["127.0.0.1:7080"] - 12
保持版本更新
停止、替换二进制文件、再启动。节点各自独立升级,网络无需协同停机,期间缺失的 gossip 事件会在启动时自动重放。
shellsystemctl stop openfiat-node # SIGTERM, lets RocksDB flush # docker: docker compose pull node && docker compose up -d node install -m 0755 ./openfiat-node /usr/local/bin/ systemctl start openfiat-node # Missed gossip events replay automatically on start. journalctl -u openfiat-node -f - 13
备份无法再生成的内容
市场状态随时可以通过快照重新同步,而钱包无法再生成。余额与履约托管存放在 Solana 上,而不在你的磁盘中。
shell# Back up what cannot be re-derived. Marketplace state can always be # re-synced from a snapshot; the wallet cannot be regenerated. tar czf openfiat-wallet-$(date +%F).tar.gz /etc/openfiat/wallet.json # Authoritative financial state lives on Solana, not on the node's own # RocksDB volume.
出现问题时
没有对等节点连接
UDP 4001 被拦截,或 --entrypoint 指向了对等节点实际无法访问的地址——它必须是静态的 multiaddr/IP,而不是主机名(DNS 引导地址无法解析)。节点开始监听后会在日志中打印它自己可被访问的地址。
ss -lunp | grep 4001 && journalctl -u openfiat-node -n 100同步始终无法完成
快照导入(OFS-1300)尚未完成,或还没有对等节点公告足够新的快照——可检查 getLatestSnapshot / getCheckpointHeight。
journalctl -u openfiat-node -n 200 --no-pager | grep -i snapshot磁盘占用持续增长
RocksDB 尚未压缩。
du -sh /var/lib/openfiat/*签名或有效期被拒绝
时钟漂移。记录带有时间戳与有效期,因此主机时钟必须准确。
chronyc tracking节点应当暴露的信息
这就是节点目前导出的全部指标——已连接对等节点数、链路模式与同步进度尚未成为 Prometheus 指标,只能通过 JSON-RPC 轮询获得(getChainStatus、getLatestSnapshot、getCheckpointHeight)。
基础设施
CPU · 内存 · 磁盘 · 网络吞吐
协议(GET /metrics)
rpc_requests_total · rpc_errors_total
自行托管用户界面
OpenFiat 将协议与界面分离。任何人都可以托管网页界面、移动网关、企业门户或区域市场,而它们访问的都是同一个市场。
无论使用哪个界面,每位参与者面对的都是同一个去中心化市场。
官方网页界面 · 社区界面 · 商户界面 · 区域市场 · 企业门户
界面连接的对象
界面并不直接与链通讯。它连接到一个或多个邻近的 OpenFiat 节点,并在某个节点不可用时自动切换到另一个。
节点 API 范围
- 市场搜索挂单 · 创建挂单 · 更新挂单 · 下架挂单
- 交易预订挂单 · 同步交易会话 · 提交付款确认
- 基础设施节点发现 · 网关发现 · 快照信息 · 风险情报
- 治理提案发现 · 提交投票 · 国库信息
所有合规节点暴露的 API 完全一致,因此针对某个节点开发的界面同样适用于其他节点。