Запуск узла
Узел запускается в три шага: поднять контейнер, зарегистрировать звезду в блокчейне, проверить, что всё подключилось. Дальше он работает и зарабатывает сам.
Открытая альфа работает в Arbitrum Sepolia. Chain id
421614, образsingulanetwork/star:alpha. ETH на газ можно взять в кране Arbitrum Sepolia. Настоящие деньги не нужны.
Что понадобится
Заголовок раздела «Что понадобится»- Linux-сервер с публичным IP.
- Открытые входящие порты TCP 443, 8443, 2053 и 2083. Нужны все четыре, зачем — в разделе пресеты маскировки.
- Кошелёк с небольшим количеством Sepolia ETH на одну транзакцию. Он станет владельцем звезды и будет получать выплаты. На сервер его ключ не попадает.
Шаг 1 — запустить узел
Заголовок раздела «Шаг 1 — запустить узел»Быстрее всего — установщиком:
curl -fsSL https://stargate.singula.network/install-node.sh | sudo bashОн проверит, что есть Docker и свободны четыре порта, спросит код страны, поднимет контейнер и напечатает адрес подписанта, который понадобится на шаге 2. Если запустить его ещё раз, он обновит образ и сохранит личность узла.
Docker установщик не ставит и обратный прокси не настраивает. REALITY сам завершает TLS и должен видеть исходный ClientHello, поэтому входы узла занимают порты хоста напрямую.
Ниже — то же самое вручную.
Программа star одна для всех ролей и при запуске читает свою роль из StarRegistry. При первом запуске роли ещё нет: узел создаёт хранилище, печатает свою личность и ждёт регистрации.
Всё, что принадлежит узлу, лежит в одном каталоге: хранилище, ключ, которым оно зашифровано, и первый час после запуска — фраза восстановления. Монтируйте этот каталог целиком в /var/lib/star.
Сначала создайте мастер-секрет. Без него узел не запустится, а сам он его не придумывает: если бы он молча создал новый секрет, всё сохранённое под старым стало бы недоступно.
mkdir -p ~/.singulachmod 700 ~/.singulaopenssl rand -hex 32 > ~/.singula/master_keychmod 600 ~/.singula/master_keyservices: star-node: image: singulanetwork/star:alpha container_name: singula-star restart: unless-stopped network_mode: 'host' # От вашего имени, чтобы файлы в монтировании остались вашими. Сам по себе # образ переходит на собственного пользователя, а тот не может писать в # чужой каталог — и узел падает на первом же действии, открытии хранилища. user: "${UID}:${GID}" volumes: - ~/.singula:/var/lib/star environment: - MASTER_SECRET_FILE=/var/lib/star/master_key - BLOCKCHAIN_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc - CHAIN_ID=421614 - NODE_COUNTRY_CODE=DE # - REVERSE_PROXY=node-de.example.com:443UID="$(id -u)" GID="$(id -g)" docker compose up -d && docker compose logs -fRoot не нужен нигде, даже для порта 443: Docker выставляет net.ipv4.ip_unprivileged_port_start в 0, а у xray в образе есть файловая capability на привязку портов. Нужен только пользователь, которому можно работать с Docker: usermod -aG docker $USER.
network_mode: host — самый простой способ открыть четыре порта и правильно определить публичный IP.
Без Compose:
docker run -d --name singula-star --network host \ --user "$(id -u):$(id -g)" \ -v ~/.singula:/var/lib/star \ -e MASTER_SECRET_FILE=/var/lib/star/master_key \ -e BLOCKCHAIN_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc \ -e CHAIN_ID=421614 -e NODE_COUNTRY_CODE=DE \ singulanetwork/star:alphaМожно и без Docker — запустить бинарник под systemd с BADGER_DIR=/var/lib/star/badger и теми же переменными.
Настройки, которые стоит знать
Заголовок раздела «Настройки, которые стоит знать»| Переменная | По умолчанию | Что делает |
|---|---|---|
NODE_COUNTRY_CODE | XX | Страна узла. Учитывается, только когда клиент просит конкретную страну |
NODE_MAX_SESSIONS | 100 | Сколько сессий узел держит одновременно |
REVERSE_PROXY | — | Публичный host:port, по которому клиенты должны вас находить, если он отличается от адреса машины |
METRICS_HTTP_PORT | 9101 | Порт /status (JSON) и /metrics (Prometheus); 0 выключает |
METRICS_BIND | 127.0.0.1 | Где он слушает. Расширяйте только за файрволом или паролем — см. ниже |
BADGER_DIR | /var/lib/star/badger | Локальное хранилище |
Шаг 2 — зарегистрировать звезду
Заголовок раздела «Шаг 2 — зарегистрировать звезду»В логах узел печатает баннер с адресом подписанта — это его горячий ключ:
STAR — MANUAL REGISTRATION REQUIRED Signer address : 0x...Если баннер уже уехал из лога, тот же адрес отдаёт локальная страница состояния:
curl -s localhost:9101/status | jq -r .info.signerВ Stargate подключите кошелёк, который будет владельцем звезды, откройте Stars, нажмите + Register Star, вставьте адрес подписанта и подпишите транзакцию.
Это вызов StarRegistry.register(signer). Ни выбора роли, ни очереди: каждая новая звезда — узел. Один адрес подписанта может принадлежать только одной звезде.
Перезапускать ничего не нужно. Узел сам следит за блокчейном и подхватит свой starId, когда транзакция пройдёт.
Шаг 3 — проверить
Заголовок раздела «Шаг 3 — проверить»На карточке звезды в Stargate должны быть роль Node, зелёный индикатор, публичный адрес и свежий heartbeat — узел шлёт его каждые 30 секунд.
В логах узла появится Registered with CP: он нашёл живой Control Plane в CPEndpointRegistry, держит с ним соединение и ждёт сессий.
Как следить за узлом
Заголовок раздела «Как следить за узлом»Узел рассказывает о себе по двум адресам на одном порту: /status отдаёт JSON, /metrics — формат Prometheus. Цифры одни и те же: сколько сессий он везёт, есть ли связь с Control Plane, нашёл ли он себя в блокчейне, сколько работает, сколько ест процессора и памяти.
Рядом с цифрами лежит блок info: адрес подписанта, starId, страна, ключи REALITY, реестр и провайдер RPC. Ключ из адреса RPC вырезан. В Prometheus те же поля приходят метками singula_info.
curl -s localhost:9101/status | jqСтраница слушает только localhost, и так лучше оставить. В ответе написано role: node, а метрики называются singula_node_*, так что по одному запросу к открытому порту видно, что здесь узел. А узлы как раз стараются не выдавать себя: четыре маскировочных пресета, имитация популярного сайта на 443, ни одного адреса узла в блокчейне.
Если нужно, расширьте доступ через METRICS_BIND, но только за файрволом, списком разрешённых адресов или обратным прокси с паролем. Узел напишет об этом в лог.
Пресеты маскировки
Заголовок раздела «Пресеты маскировки»Узел всегда держит четыре входа REALITY, по одному на регион. Каждый слушает свой порт и выглядит как обращение к сайту, популярному в этом регионе. Набор зашит в программу, отключить пресет нельзя.
| Пресет | Порт | Сайт-прикрытие | Имена серверов |
|---|---|---|---|
default | 443 | www.adobe.com | www.adobe.com, helpx.adobe.com, assets.adobe.com |
RU | 8443 | yandex.net | yandex.net, yastatic.net, mc.yandex.ru |
CN | 2053 | www.qq.com | www.qq.com, weixin.qq.com, www.gtimg.com |
IR | 2083 | www.aparat.com | www.aparat.com, www.digikala.com, asredanesh.com |
Пресет выбирает клиент. Если он ничего не попросил, узел берёт default: где находится клиент, в сети никто не выясняет.
Каждый пресет доступен ещё и по xHTTP на том же порту, по одному пути, постоянному для этого узла и пресета. Клиент, которому мешают по голому TCP, переключается на xHTTP без новой сессии.
Откройте все четыре порта. Закрытый порт не выключает пресет — просто клиенты из этого региона не смогут подключиться.
За обратным прокси
Заголовок раздела «За обратным прокси»REALITY сам завершает TLS и должен видеть исходный ClientHello, поэтому прокси перед узлом может только маршрутизировать соединения, но не расшифровывать их. Используйте маршрутизацию по SNI со сквозным TLS:
# TLS passthrough, SNI → нужный входящийservices: star: image: singulanetwork/star:alpha restart: unless-stopped user: "${UID}:${GID}" networks: [public] volumes: - ~/.singula:/var/lib/star environment: - MASTER_SECRET_FILE=/var/lib/star/master_key - BLOCKCHAIN_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc - CHAIN_ID=421614 - NODE_COUNTRY_CODE=DE - REVERSE_PROXY=node.example.com:443 labels: - "traefik.enable=true" - "traefik.docker.network=public" - "traefik.tcp.routers.star-default.rule=HostSNI(`www.adobe.com`) || HostSNI(`helpx.adobe.com`) || HostSNI(`assets.adobe.com`)" - "traefik.tcp.routers.star-default.entrypoints=websecure" - "traefik.tcp.routers.star-default.tls.passthrough=true" - "traefik.tcp.routers.star-default.service=star-default" - "traefik.tcp.services.star-default.loadbalancer.server.port=443" - "traefik.tcp.routers.star-ru.rule=HostSNI(`yandex.net`) || HostSNI(`yastatic.net`) || HostSNI(`mc.yandex.ru`)" - "traefik.tcp.routers.star-ru.entrypoints=websecure" - "traefik.tcp.routers.star-ru.tls.passthrough=true" - "traefik.tcp.routers.star-ru.service=star-ru" - "traefik.tcp.services.star-ru.loadbalancer.server.port=8443" - "traefik.tcp.routers.star-cn.rule=HostSNI(`www.qq.com`) || HostSNI(`weixin.qq.com`) || HostSNI(`www.gtimg.com`)" - "traefik.tcp.routers.star-cn.entrypoints=websecure" - "traefik.tcp.routers.star-cn.tls.passthrough=true" - "traefik.tcp.routers.star-cn.service=star-cn" - "traefik.tcp.services.star-cn.loadbalancer.server.port=2053" - "traefik.tcp.routers.star-ir.rule=HostSNI(`www.aparat.com`) || HostSNI(`www.digikala.com`) || HostSNI(`asredanesh.com`)" - "traefik.tcp.routers.star-ir.entrypoints=websecure" - "traefik.tcp.routers.star-ir.tls.passthrough=true" - "traefik.tcp.routers.star-ir.service=star-ir" - "traefik.tcp.services.star-ir.loadbalancer.server.port=2083"
traefik: image: traefik:v3.1 restart: unless-stopped command: - --providers.docker=true - --providers.docker.exposedbydefault=false - --entrypoints.websecure.address=:443 ports: ["443:443"] volumes: - /var/run/docker.sock:/var/run/docker.sock:ro networks: [public]
networks: public: external: true# Один публичный порт; HAProxy разводит по SNI, не терминируя TLS.services: star: image: singulanetwork/star:alpha restart: unless-stopped user: "${UID}:${GID}" networks: [edge] volumes: - ~/.singula:/var/lib/star environment: - MASTER_SECRET_FILE=/var/lib/star/master_key - BLOCKCHAIN_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc - CHAIN_ID=421614 - NODE_COUNTRY_CODE=DE expose: ["443", "8443", "2053", "2083"]
haproxy: image: haproxy:2.9-alpine restart: unless-stopped depends_on: [star] ports: ["443:443"] volumes: - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro networks: [edge]
networks: edge: {}
# haproxy.cfg — без терминации TLS, REALITY должен видеть ClientHello# global# log stdout format raw local0# defaults# mode tcp# timeout connect 5s# timeout client 1h# timeout server 1h# frontend tls_in# bind *:443# tcp-request inspect-delay 5s# tcp-request content accept if { req_ssl_hello_type 1 }# acl is_ru req_ssl_sni -m end yandex.net yastatic.net mc.yandex.ru# acl is_cn req_ssl_sni -m end qq.com gtimg.com# acl is_ir req_ssl_sni -m end aparat.com digikala.com asredanesh.com# use_backend ru if is_ru# use_backend cn if is_cn# use_backend ir if is_ir# default_backend def# backend def# server star_def star:443# backend ru# server star_ru star:8443# backend cn# server star_cn star:2053# backend ir# server star_ir star:2083Личность и фраза восстановления
Заголовок раздела «Личность и фраза восстановления»Все ключи узла — подписанта, REALITY, сессий — выводятся из одного секрета, который создаётся при первом запуске. Рядом записывается фраза восстановления из 24 слов, /var/lib/star/recovery-phrase.txt, и через час она удаляется сама. Скопируйте её сразу: больше она нигде не хранится.
Чтобы восстановить узел, укажите файл с фразой в NODE_MNEMONIC_FILE. Можно и через NODE_MNEMONIC, но файл надёжнее: переменная видна в docker inspect и в compose-файлах, которые попадают в репозитории. Если у фразы есть пароль, добавьте NODE_MNEMONIC_PASSPHRASE; другой пароль даст другой узел.
Фраза восстанавливает те же ключи, поэтому звезду заново регистрировать не нужно: адрес подписанта совпадёт с тем, что уже записан в блокчейне.
Сами ключи хранятся зашифрованными, ключом из файла MASTER_SECRET_FILE — ~/.singula/master_key, если вы шли по шагам выше, или /run/secrets/singula_master_key, если ничего не задавали. Потерять этот файл не страшно, пока у вас есть фраза. Если пропали оба, узлу понадобится новая личность, а владелец перепривяжет подписанта вызовом updateSigner.
Что дальше
Заголовок раздела «Что дальше»Control Plane начнёт направлять к вам сессии. Узел выбирается по загрузке, свежести heartbeat и случайной добавке — но не по тому, где клиент: этого Control Plane не знает. NODE_COUNTRY_CODE важен, только когда клиент сам попросил вашу страну.
Каждые четыре часа и ещё раз незадолго до конца эпохи Control Plane склеивает отработанные вами интервалы, просит узел подписать каждый и отправляет их в EpochAttestations. Платят за время, а не за объём, но время засчитывается не всегда: оно копится, пока с последнего засчитанного чекпоинта клиента не пройдёт 64 КиБ, а если за 10 минут столько не набралось, сгорает.
Когда эпоха закроется, claimNode выплатит USDC на кошелёк владельца. Вызвать его может кто угодно — получатель всё равно берётся из реестра, — так что ETH на кошельке держать не обязательно.
Секунда оплачивается по фиксированной ставке: сейчас около 0.2333 sUSDC за сутки работы с одной сессией. Подробнее — в экономике.
Если сессий нет, это нормально для тихой сети: значит, их сейчас мало во всей сети, а не что у вас неудачный регион.
