Запуск узла OpenFiat
Операторы узлов поддерживают децентрализованный рынок, лежащий поверх блокчейна. Валидаторы Solana защищают цепочку и исполняют программы OpenFiat; узлы несут объявления, репутацию, сообщения и поиск.
Команды ниже используют эталонное развёртывание: сервис openfiat-node, /etc/openfiat для конфигурации и /var/lib/openfiat для данных.
Оборудование
Минимум
4 ядра CPU · 16 ГБ ОЗУ · SSD NVMe 250 ГБ · Стабильное широкополосное соединение
Рекомендуется для продакшена
8–16 ядер CPU · 32 ГБ ОЗУ или больше · SSD NVMe 1 ТБ · Сеть с высокой пропускной способностью и низкой задержкой · Защита питания через ИБП (UPS) · Резервное интернет-подключение
Узел — единый бинарник на Rust. libp2p (QUIC, Noise, Yamux) и RocksDB скомпилированы внутри — отдельно устанавливать нечего.
Где его запускать
AllenHark ведёт первоначальную разработку протокола и продаёт две вещи, которые нужны узлу: сервер и RPC-доступ к Solana. Операторы узлов получают 50% скидки на оба в рамках программы OpenFiat.
Хостинг серверов
Хранилище NVMe на каналах 10 Гбит/с во Франкфурте, Амстердаме и Чикаго. Рассчитывайте по оборудованию выше — хранилище это то, что полноценный узел постепенно требует, поэтому скажите, что вы запускаете, и конфигурацию подберут для вас.
Смотреть тарифыRPC и gRPC для Solana
Соединения со стейком, чтобы транзакции, отправляемые вашим узлом, не стояли в очереди за чужими. Доступ выдаётся по списку разрешённых IP, а не по API-ключу. Yellowstone gRPC доступен для стриминга обновлений аккаунтов и слотов.
Смотреть тарифыКак получить скидку
Никакого кода вводить не нужно. Спросите в Discord или в чате на allenhark.com, скажите, что запускаете узел OpenFiat, и скидка применяется к вашему заказу.
Служите сети, а не только себе
Узел, который только читает, — гость. Узел, до которого может достучаться публика, — это инфраструктура: кошелькам, эксплорерам и веб-приложениям нужно с *кем-то* разговаривать, а таких «кого-то» сегодня немного. Каждый оператор, который ставит узел за TLS и задаёт --public-rpc-url, расширяет этот набор, а это и есть разница между сетью с несколькими независимыми точками входа и сетью с единой точкой отказа, от которой все молча зависят. Это стоит сертификата и обратного прокси. И это вознаграждается: достижимый узел — тот, которому пиры могут бросить вызов и вознаградить, и собственные приложения OpenFiat будут использовать ваш наравне со всеми.
Две вещи, которые стоит знать заранее. Обслуживание публики означает реальный трафик, так что рассчитайте машину под это и следите за эндпоинтом метрик. И публичный узел — это публичное обязательство: люди будут строить против URL, который вы публикуете, поэтому выводите его из работы обдуманно, а не молча.
Установка
Сборка из исходников — для продакшена, сегодня
Ни одна версия ещё не помечена тегом, так что сейчас это единственный способ получить бинарник узла. Нужны тулчейн Rust и тулчейн C для RocksDB. Соберите, положите в /usr/local/bin и запустите под systemd — это то развёртывание, которое документирует эта страница, и то, что следует использовать для узла, от которого зависят другие.
# 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/Готовый бинарник — как только версия будет помечена тегом
Пуш тега v* запускает workflow релиза, который собирает openfiat-node на нативных раннерах и публикует архивы linux-x86_64 и windows-x86_64 в GitHub Releases. Пока ничего не помечено тегом, поэтому эта страница сейчас пуста. Учтите, что это обычные релизные сборки, не статические и не подписанные — проверяйте то, что скачиваете, против workflow, который это произвёл.
Релизы на GitHubuseradd --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Порты
- 4001/UDPТрафик между пирами. QUIC — основной транспорт, поэтому этот UDP-порт должен быть достижим — тот, что чаще всего забывают открыть.Должен быть достижим
- 7080/TCPJSON-RPC, WebSocket, REST, health и метрики — всё обслуживает один реальный порт, отдельного порта на каждую поверхность нет.Должен быть достижим
- 7080/TCPТот же порт, что и API выше (GET /metrics). Ограничьте фаерволом до loopback/приватной сети, если не хотите, чтобы он был публичным.Держите закрытым
Вывод узла в онлайн
Выполняйте эти шаги по порядку. Каждый шаг ниже приводит нужную команду.
- 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
Сгенерируйте кошелёк узла
Отдельного формата «идентичности узла» нет — идентичность узла это настоящий wallet.json из CLI Solana, тот же файл, что производит solana-keygen. Его seed переиспользуется и для 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 нет собственного файла конфигурации — каждая настройка это переменная окружения, читаемая один раз при запуске: где лежат данные, на каком адресе слушать, к каким пирам подключаться на старте и (по желанию) какой RPC-эндпоинт Solana использовать. Bootstrap-пиры должны быть статическим multiaddr/IP, а не именем хоста — bootstrap по 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
Откройте нужные порты
Пиры достигают узла по UDP, потому что QUIC — основной транспорт — это тот порт, который чаще всего забывают. Один TCP-порт обслуживает JSON-RPC, WebSocket, REST, health и метрики вместе; держите его закрытым, если не хотите обслуживать клиентов публично.
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 впереди и получите сертификат, затем сообщите узлу его публичный URL через --public-rpc-url, и он анонсирует себя, чтобы кошельки, эксплореры и само веб-приложение OpenFiat могли им пользоваться. Порядок важен: сначала nginx по простому HTTP, затем 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 подтверждает, что процесс работает; getChainStatus по JSON-RPC говорит, GossipOnly он или RpcConnected, и его текущий blockhash, если он последний.
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
Он уже часть сети
Отдельного шага «анонсировать» нет — как только у узла есть bootstrap-пиры, он распространяется через gossip и распространяется автоматически; никто его не одобряет. Стейкинг, публикация метаданных в реестре сервисов, присоединение к спорам и подача голосов управления — это отдельные действия, управляемые кошельком, которые клиент выполняет против работающего узла — смотрите соответствующее руководство по участию для каждого.
Узел стейкает OPEN, чтобы стать активным участником. Но стейк не покупает репутацию: оператор с плохими показателями не может это компенсировать бо́льшим стейком. Эффективный приоритет сочетает репутацию, стейк и измеренную сетевую производительность.
- 11
Следите за ним
Сигналы, которые действительно предсказывают проблемы, — это подключённые пиры, режим цепочки (GossipOnly или RpcConnected) и возраст blockhash. Настройте оповещения по ним, и вы узна́ете раньше своих пользователей.
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, а не имя хоста (bootstrap по 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-поверхность, поэтому интерфейс, написанный против одного узла, работает против всех.